diff --git a/README.md b/README.md index 26196f6..dd342be 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ json-query-wrapper is a wrapper for the popular command-line JSON processor "[jq ## Installation ```bash -$ composer require invoicesharing/json-query-wrapper +$ composer require estahn/json-query-wrapper ``` ## Usage @@ -58,6 +58,31 @@ $jq->setDataProvider(new JsonQueryWrapper\DataProvider\File('test.json'); $jq->run('.Foo.Bar == "33"'); # Returns bool(true) ``` +## Command Line Options + +This enables passing additional command line options to `jq` to customize output and behavior. Currently, the following options are supported: + +| Option | `jq` Option | Default? | Description | +| --- | --- | --- | --- | +| OPTION_EXIT_BASED_ON_OUTPUT | -e | no | set exit status based on whether output was successful | +| OPTION_INPUT_RAW | -R | no | don't parse input as JSON | +| OPTION_NULL_INPUT | -n | no | don't read any input at all | +| OPTION_OUTPUT_COLORIZE | -C | no | colorize output on shell | +| OPTION_OUTPUT_COMPACT | -c | no | compact output, don't pretty print | +| OPTION_OUTPUT_JOIN | -j | no | join each line of output, don't print newline | +| OPTION_OUTPUT_MONOCHROME | -M | yes | don't colorize output on the shell, print plainly | +| OPTION_OUTPUT_RAW | -r | no | write output directly, don't convert to JSON | +| OPTION_OUTPUT_SORT_KEYS | -S | no | sort keys in the output | + +If not explicitly set, only the monochrome option will be set. More information about all these command line options can be found in the [jq manual](https://stedolan.github.io/jq/manual/#Invokingjq). To use command line options, just pass an array to the `JsonQueryFactory::create` or `JsonQueryFactory::createWith` methods: + +```php +$options = [JsonQueryWrapper\CommandLineOption\CommandLineOption::OPTION_OUTPUT_JOIN, JsonQueryWrapper\CommandLineOption\CommandLineOption::OPTION_OUTPUT_SORT_KEYS]; +$jq = JsonQueryWrapper\JsonQueryFactory::createWith('test.json', $options); +$jq->run('.Foo.Bar'); # string(33) +``` + + ## Data Providers A "Data Provider" provides the wrapper with the necessary data to read from. It's a common interface for several providers. All providers implement the `DataProviderInterface` which essentially returns a path to the file for `jq`. diff --git a/src/JsonQueryWrapper/CommandLineOption/CommandLineOption.php b/src/JsonQueryWrapper/CommandLineOption/CommandLineOption.php new file mode 100644 index 0000000..c191b80 --- /dev/null +++ b/src/JsonQueryWrapper/CommandLineOption/CommandLineOption.php @@ -0,0 +1,47 @@ +options = []; + foreach ($options as $option) { + $this->addOption($option); + } + } + + public function addOption(string $option): void + { + if (!in_array($option, $this->options) && static::isValidOption($option)) { + $this->options[] = $option; + } + } + + public static function isValidOption(string $option): bool + { + $reflector = new \ReflectionClass(CommandLineOptionInterface::class); + $availableOptions = array_values($reflector->getConstants()); + return in_array($option, $availableOptions); + } + + public function getOptionsAsString(): ?string + { + return empty($this->options) ? null : '-' . implode('', $this->options); + } + + public function removeOption(string $option): void + { + $optionIndex = array_keys($this->options, $option)[0] ?? null; + if ($optionIndex !== null) { + unset($this->options[$optionIndex]); + $this->options = array_values($this->options); + } + } +} \ No newline at end of file diff --git a/src/JsonQueryWrapper/CommandLineOption/CommandLineOptionInterface.php b/src/JsonQueryWrapper/CommandLineOption/CommandLineOptionInterface.php new file mode 100644 index 0000000..4c2eab1 --- /dev/null +++ b/src/JsonQueryWrapper/CommandLineOption/CommandLineOptionInterface.php @@ -0,0 +1,21 @@ +processFactory = $processFactory; $this->mapper = $dataTypeMapper; + if ($commandLineOption) { + $this->commandLineOption = $commandLineOption; + } else { + $this->commandLineOption = new CommandLineOption([CommandLineOptionInterface::OPTION_OUTPUT_MONOCHROME]); + } } /** @@ -60,8 +75,7 @@ public function __construct(ProcessFactoryInterface $processFactory, DataTypeMap * @param string $filter * * @return mixed - * @throws DataProviderMissingException - * @throws Exception\DataTypeMapperException + * @throws DataProviderMissingException|\RuntimeException */ public function run($filter) { @@ -69,15 +83,23 @@ public function run($filter) throw new DataProviderMissingException('A data provider such as file or text is missing.'); } - $command = [$this->cmd, '-M', $filter, $this->dataProvider->getPath()]; - - $process = $this->processFactory->build($command); - - $process->run(); - - $result = trim($process->getOutput()); + $options = $this->commandLineOption->getOptionsAsString(); + if ($options !== null) { + $command = [$this->cmd, $options, $filter, $this->dataProvider->getPath()]; + } else { + $command = [$this->cmd, $filter, $this->dataProvider->getPath()]; + } - return $this->mapper->map($result); + try { + $process = $this->processFactory->build($command); + $process->mustRun(); + $result = trim($process->getOutput()); + return $this->mapper->map($result); + } catch (ProcessFailedException $processFailedException) { + throw new \RuntimeException(trim($process->getErrorOutput()), $processFailedException->getProcess()->getExitCode(), $processFailedException); + } catch (DataTypeMapperException $dataTypeMapperException) { + throw new \RuntimeException($dataTypeMapperException->getMessage(), $dataTypeMapperException->getCode(), $dataTypeMapperException); + } } /** diff --git a/src/JsonQueryWrapper/JsonQueryFactory.php b/src/JsonQueryWrapper/JsonQueryFactory.php index 5a33826..e1531e6 100644 --- a/src/JsonQueryWrapper/JsonQueryFactory.php +++ b/src/JsonQueryWrapper/JsonQueryFactory.php @@ -11,34 +11,40 @@ namespace JsonQueryWrapper; +use JsonQueryWrapper\CommandLineOption\CommandLineOption; use JsonQueryWrapper\DataProvider\File; use JsonQueryWrapper\DataProvider\Text; use JsonQueryWrapper\Process\ProcessFactory; class JsonQueryFactory { + const DEFAULT_OPTIONS = [CommandLineOption::OPTION_OUTPUT_MONOCHROME]; + /** * Creates a JsonQuery object without data provider. * + * @param array $options command line options + * * @return JsonQuery */ - public static function create() + public static function create($options = self::DEFAULT_OPTIONS) { - return new JsonQuery(new ProcessFactory(), new DataTypeMapper()); + return new JsonQuery(new ProcessFactory(), new DataTypeMapper(), new CommandLineOption($options)); } /** * Creates a JsonQuery object with data provider. * * @param string $filenameOrText A path to a json file or json text + * @param array $options command line options * * @return JsonQuery */ - public static function createWith($filenameOrText) + public static function createWith($filenameOrText, $options = self::DEFAULT_OPTIONS) { $provider = file_exists($filenameOrText) ? new File($filenameOrText) : new Text($filenameOrText); - $jq = new JsonQuery(new ProcessFactory(), new DataTypeMapper()); + $jq = new JsonQuery(new ProcessFactory(), new DataTypeMapper(), new CommandLineOption($options)); $jq->setDataProvider($provider); return $jq; diff --git a/tests/JsonQueryWrapper/CommandLineOptionTest.php b/tests/JsonQueryWrapper/CommandLineOptionTest.php new file mode 100644 index 0000000..cfaf88b --- /dev/null +++ b/tests/JsonQueryWrapper/CommandLineOptionTest.php @@ -0,0 +1,43 @@ +assertNull($options->getOptionsAsString()); + } + + public function testAddingCommandLineOptions() + { + $options = new CommandLineOption(); + $options->addOption(CommandLineOption::OPTION_OUTPUT_JOIN); + $options->addOption(CommandLineOption::OPTION_OUTPUT_SORT_KEYS); + $this->assertEquals($options->getOptionsAsString(), '-jS'); + } + + public function testAddingDuplicateCommandLineOptions() + { + $options = new CommandLineOption(); + $options->addOption(CommandLineOption::OPTION_OUTPUT_JOIN); + $options->addOption(CommandLineOption::OPTION_OUTPUT_SORT_KEYS); + $this->assertEquals($options->getOptionsAsString(), '-jS'); + $options->addOption(CommandLineOption::OPTION_OUTPUT_JOIN); + $options->addOption(CommandLineOption::OPTION_OUTPUT_SORT_KEYS); + $this->assertEquals($options->getOptionsAsString(), '-jS'); + } + + public function testRemovingCommandLineOptions() + { + $options = new CommandLineOption(); + $options->addOption(CommandLineOption::OPTION_OUTPUT_JOIN); + $options->addOption(CommandLineOption::OPTION_OUTPUT_SORT_KEYS); + $this->assertEquals($options->getOptionsAsString(), '-jS'); + $options->removeOption(CommandLineOption::OPTION_OUTPUT_SORT_KEYS); + $this->assertEquals($options->getOptionsAsString(), '-j'); + } +} \ No newline at end of file diff --git a/tests/JsonQueryWrapper/DataTypeMapperTest.php b/tests/JsonQueryWrapper/DataTypeMapperTest.php index 1edca83..59169e9 100644 --- a/tests/JsonQueryWrapper/DataTypeMapperTest.php +++ b/tests/JsonQueryWrapper/DataTypeMapperTest.php @@ -78,10 +78,8 @@ public function testNull() public function testJson() { - $this->markTestIncomplete('Need to decide whether to convert JSON data'); - - $json = ['Foo' => ['Bar' => 33]]; - $this->assertEquals('Foo', $this->mapper->map(json_encode($json))); + $json = (object)['Foo' => (object)['Bar' => 33]]; + $this->assertEquals($json, $this->mapper->map(json_encode($json))); } public function testGarbage() diff --git a/tests/JsonQueryWrapper/JsonQueryTest.php b/tests/JsonQueryWrapper/JsonQueryTest.php index 1116df0..da51192 100644 --- a/tests/JsonQueryWrapper/JsonQueryTest.php +++ b/tests/JsonQueryWrapper/JsonQueryTest.php @@ -29,6 +29,8 @@ public function testCmdChange() $dataProvider = new Text(json_encode($json)); $process = $this->createMock(Process::class); + $process + ->method('mustRun'); $process ->method('run'); @@ -49,7 +51,7 @@ public function testCmdChange() ); $sut->setDataProvider($dataProvider); - static::assertEquals( + $this->assertEquals( $expected, $sut->run('.name') );