signature = 'bundle:validate { --r|recursive : Also render sub-bundles } { --source-disk= : Use specified content disk - Defaults to ' . env('CONTENT_DISK') . ' } { path? : Path to a specific bundle to render - Default to / } '; parent::__construct(); } /** * Execute the console command. */ public function handle() { $this->selectDisk() ->validate() ->showReport(); } /** * Select the disk we will be working on */ private function selectDisk(): self { $sourceDisk = $this->option('source-disk') ?? env('CONTENT_DISK'); $this->sourceDisk = Storage::disk($sourceDisk); $this->comment( sprintf( 'Using `%s` as source disk', $sourceDisk ) ); return $this; } /** * Collect a list of bundles to validate */ private function getBundles() { $path = $this->argument('path') ?? '/'; $comment = sprintf('Validating %s', $path); if ($this->option('recursive')) { $comment .= ' and all sub-bundles'; } $this->comment($comment); $this->output->write('Collecting bundles... '); if ($this->option('recursive')) { $bundles = Bundle::findBundles($this->sourceDisk, $path, true); } else { $bundles = [new Bundle($path, $this->sourceDisk)]; } $this->info('OK'); return $bundles; } /** * Return an associative array containing available bundles metadata files * as keys and json-decoded validation schemas as values */ private function getValidators() { return array_map(function ($path) { return json_decode(file_get_contents($path)); }, config('json_validator')); } /** * Perform json validation on selected bundles */ private function validate(): self { $bundles = $this->getBundles(); $validators = $this->getValidators(); progress( label: 'Validating... ', steps: $bundles, callback: fn (Bundle $bundle, $progress) => $this->handleBundle($bundle, $progress, $validators) ); return $this; } /** * Handle specific bundle */ private function handleBundle(Bundle $bundle, $progress, $validators) { $progress->label(sprintf('Validating %s ...', $bundle->getPath())); foreach ($validators as $metadataFilename => $schema) { $progress->hint(sprintf('Validating %s ...', $metadataFilename)); $filepath = $bundle->metadata($metadataFilename)->getFilename(); if (!$this->sourceDisk->exists($filepath)) { continue; } $source = $this->sourceDisk->get($filepath) ?? '{}'; $data = json_decode($source); $validator = new Validator(); $validator->validate($data, $schema); if (!$validator->isValid()) { $this->invalid[$bundle->getPath()] = [ 'metadata' => $metadataFilename, 'errors' => $validator->getErrors(), ]; } } } /** * Show a report in case of need */ private function showReport() { if (empty($this->invalid)) { $this->info('All files are valid'); } else { $count = count($this->invalid); $this->error(sprintf('%d invalid %s reported', $count, Str::plural('file', $count))); $this->newLine(2); foreach ($this->invalid as $bundlePath => $data) { $this->line($bundlePath); $this->line(sprintf(' > In %s:', $data['metadata'])); foreach ($data['errors'] as $error) { $this->line(sprintf(' - %s', $error['message'])); } } } } }