diff --git a/README.md b/README.md index 104f6495..f8784850 100644 --- a/README.md +++ b/README.md @@ -960,10 +960,10 @@ In fact, this can be considered as partial inheritance. **If something went wrong** If you're having trouble working with presets and don't understand how the CSV Blueprint under the hood understands it, -just add `--dump-schema` to see it. Also, there is a separate CLI command for validating schema: +just add `--dump-schema` to see it. Also, there is a separate CLI command to dump schema: ```shell -./csv-blueprint validate:schema --dump-schema --schema=./your/schema.yml +./csv-blueprint debug-schema -s ./your/schema.yml ``` @@ -1542,6 +1542,49 @@ Options: +`./csv-blueprint dump-schema --help` + +
+ CLICK to see debug-schema help messege + + +``` +Description: + Show the internal representation of the schema taking into account presets. + +Usage: + debug-schema [options] + +Options: + -s, --schema=SCHEMA Specify the path to a schema file, supporting YAML, JSON, or PHP formats. + Examples: /full/path/name.yml; p/file.yml + -d, --hide-defaults Hide default values in the output. + --no-progress Disable progress bar animation for logs. It will be used only for text output format. + --mute-errors Mute any sort of errors. So exit code will be always "0" (if it's possible). + It has major priority then --non-zero-on-error. It's on your own risk! + --stdout-only For any errors messages application will use StdOut instead of StdErr. It's on your own risk! + --non-zero-on-error None-zero exit code on any StdErr message. + --timestamp Show timestamp at the beginning of each message.It will be used only for text output format. + --profile Display timing and memory usage information. + --output-mode=OUTPUT-MODE Output format. Available options: + text - Default text output format, userfriendly and easy to read. + cron - Shortcut for crontab. It's basically focused on human-readable logs output. + It's combination of --timestamp --profile --stdout-only --no-progress -vv. + logstash - Logstash output format, for integration with ELK stack. + [default: "text"] + --cron Alias for --output-mode=cron. Deprecated! + -h, --help Display help for the given command. When no command is given display help for the list command + -q, --quiet Do not output any message + -V, --version Display this application version + --ansi|--no-ansi Force (or disable --no-ansi) ANSI output + -n, --no-interaction Do not ask any interactive question + -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug +``` + + +
+ + `./csv-blueprint create:schema --help` It's beta. Work in progress. @@ -1599,6 +1642,7 @@ Options: + ## Report examples The validation process culminates in a human-readable report detailing any errors identified within the CSV file. While diff --git a/src/Commands/DebugSchema.php b/src/Commands/DebugSchema.php new file mode 100644 index 00000000..46f73824 --- /dev/null +++ b/src/Commands/DebugSchema.php @@ -0,0 +1,65 @@ +setName('debug-schema') + ->setDescription('Show the internal representation of the schema taking into account presets.') + ->addOption( + 'schema', + 's', + InputOption::VALUE_REQUIRED, + \implode("\n", [ + 'Specify the path to a schema file, supporting YAML, JSON, or PHP formats. ', + 'Examples: /full/path/name.yml; p/file.yml', + ]), + ) + ->addOption( + 'hide-defaults', + 'd', + InputOption::VALUE_NONE, + 'Hide default values in the output.', + ); + + parent::configure(); + } + + protected function executeAction(): int + { + $decorated = $this->outputMode->getOutput()->isDecorated(); + + $schemaFilename = $this->getOptString('schema'); + if (!\file_exists($schemaFilename)) { + throw new Exception("Schema file not found: {$schemaFilename}"); + } + + $this->_((new Schema($schemaFilename))->dumpAsYamlString($this->getOptBool('hide-defaults'), $decorated)); + + return self::SUCCESS; + } +} diff --git a/tests/Commands/DebugSchemaTest.php b/tests/Commands/DebugSchemaTest.php new file mode 100644 index 00000000..892eac4e --- /dev/null +++ b/tests/Commands/DebugSchemaTest.php @@ -0,0 +1,299 @@ + Tools::DEMO_YML_VALID, + ]); + + $expected = <<<'YAML' + # Schema file is "./tests/schemas/demo_valid.yml" + name: '' + description: '' + presets: [] + filename_pattern: '/(demo|demo_.*|demo-.*)\.csv$/' + csv: + header: true + delimiter: ',' + quote_char: \ + enclosure: '"' + encoding: utf-8 + bom: false + structural_rules: + strict_column_order: true + allow_extra_columns: false + columns: + - name: Name + description: '' + example: ~ + required: true + rules: + not_empty: true + length_min: 4 + length_max: 7 + aggregate_rules: + is_unique: true + + - name: City + description: '' + example: ~ + required: true + rules: + not_empty: true + is_capitalize: true + aggregate_rules: [] + + - name: Float + description: '' + example: ~ + required: true + rules: + not_empty: true + is_float: true + num_min: -19366059128 + num_max: 4825.186 + aggregate_rules: + sum_max: 4692 + + - name: Birthday + description: '' + example: ~ + required: true + rules: + not_empty: true + date_format: Y-m-d + aggregate_rules: [] + + - name: 'Favorite color' + description: '' + example: ~ + required: true + rules: + not_empty: true + allow_values: + - red + - green + - blue + aggregate_rules: [] + + YAML; + + isSame($expected, $actual); + isSame(0, $exitCode, $actual); + } + + public function testDumpHideDefaults(): void + { + [$actual, $exitCode] = Tools::virtualExecution('debug-schema', [ + 'schema' => Tools::DEMO_YML_VALID, + 'hide-defaults' => null, + ]); + + $expected = <<<'YAML' + # Schema file is "./tests/schemas/demo_valid.yml" + filename_pattern: '/(demo|demo_.*|demo-.*)\.csv$/' + columns: + - name: Name + rules: + not_empty: true + length_min: 4 + length_max: 7 + aggregate_rules: + is_unique: true + + - name: City + rules: + not_empty: true + is_capitalize: true + + - name: Float + rules: + not_empty: true + is_float: true + num_min: -19366059128 + num_max: 4825.186 + aggregate_rules: + sum_max: 4692 + + - name: Birthday + rules: + not_empty: true + date_format: Y-m-d + + - name: 'Favorite color' + rules: + not_empty: true + allow_values: + - red + - green + - blue + + YAML; + + isSame($expected, $actual); + isSame(0, $exitCode, $actual); + } + + public function testDumpPresetUsage(): void + { + [$actual, $exitCode] = Tools::virtualExecution('debug-schema', [ + 'schema' => './schema-examples/preset_usage.yml', + 'hide-defaults' => null, + ]); + + $expected = <<<'YAML' + # Schema file is "./schema-examples/preset_usage.yml" + name: 'Schema uses presets and add new columns + specific rules.' + description: 'This schema uses presets. Also, it demonstrates how to override preset values.' + presets: + users: /schema-examples/preset_users.yml + db: /schema-examples/preset_database.yml + csv: + delimiter: ; + enclosure: '|' + columns: + - name: id + description: 'Unique identifier, usually used to denote a primary key in databases.' + example: 12345 + rules: + not_empty: true + is_trimmed: true + is_int: true + num_min: 1 + aggregate_rules: + is_unique: true + sorted: + - asc + - numeric + + - name: status + description: 'Status in database' + example: active + rules: + not_empty: true + allow_values: + - active + - inactive + - pending + - deleted + + - name: login + description: "User's login name" + example: johndoe + rules: + not_empty: true + length_min: 3 + length_max: 20 + is_trimmed: true + is_lowercase: true + is_slug: true + is_alnum: true + aggregate_rules: + is_unique: true + + - name: email + description: "User's email address" + example: user@example.com + rules: + not_empty: true + is_trimmed: true + is_lowercase: true + is_email: true + aggregate_rules: + is_unique: true + + - name: full_name + description: "User's full name" + example: 'John Doe Smith' + rules: + not_empty: true + is_trimmed: true + is_capitalize: true + word_count_min: 2 + word_count_max: 8 + contains: ' ' + charset: UTF-8 + aggregate_rules: + is_unique: true + + - name: birthday + description: "Validates the user's birthday." + example: '1990-01-01' + rules: + not_empty: true + is_trimmed: true + is_date: true + date_max: now + date_format: Y-m-d + date_age_greater: 0 + date_age_less: 150 + + - name: phone + description: "User's phone number in US" + example: '+1 650 253 00 00' + rules: + not_empty: true + is_trimmed: true + starts_with: '+1' + phone: US + + - name: password + description: "User's password" + example: 9RfzE$8NKD + rules: + not_empty: true + length_min: 10 + length_max: 20 + is_trimmed: true + contains_none: + - password + - '123456' + - qwerty + - ' ' + password_strength_min: 7 + is_password_safe_chars: true + charset: UTF-8 + + - name: admin_note + description: 'Admin note' + rules: + not_empty: true + length_min: 1 + length_max: 10 + aggregate_rules: + is_unique: true + sorted: + - asc + - numeric + + YAML; + + $actual = \str_replace(PROJECT_ROOT, '', $actual); + + isSame($expected, $actual); + isSame(0, $exitCode, $actual); + } +} diff --git a/tests/ReadmeTest.php b/tests/ReadmeTest.php index 74cf09ae..a188f02d 100644 --- a/tests/ReadmeTest.php +++ b/tests/ReadmeTest.php @@ -78,6 +78,17 @@ public function testCreateSchemaHelp(): void Tools::insertInReadme('create-schema-help', $text); } + public function testDumpSchemaHelp(): void + { + $text = \implode("\n", [ + '```', + \trim(Tools::realExecution('debug-schema', ['help' => null])), + '```', + ]); + + Tools::insertInReadme('debug-schema-help', $text); + } + public function testTableOutputExample(): void { success('Replaced to image'); diff --git a/tests/Tools.php b/tests/Tools.php index c28b8767..6e368b51 100644 --- a/tests/Tools.php +++ b/tests/Tools.php @@ -18,6 +18,7 @@ use JBZoo\Cli\CliApplication; use JBZoo\CsvBlueprint\Commands\CreateSchema; +use JBZoo\CsvBlueprint\Commands\DebugSchema; use JBZoo\CsvBlueprint\Commands\ValidateCsv; use JBZoo\CsvBlueprint\Commands\ValidateSchema; use JBZoo\Utils\Cli; @@ -59,6 +60,7 @@ public static function virtualExecution(string $action, array|string $params = [ $application->add(new ValidateCsv()); $application->add(new ValidateSchema()); $application->add(new CreateSchema()); + $application->add(new DebugSchema()); $command = $application->find($action); $buffer = new BufferedOutput();