Parse and validate AT Protocol Lexicons with DTO generation for Laravel
1<?php
2
3namespace SocialDept\AtpSchema\Console;
4
5use Illuminate\Console\Command;
6use SocialDept\AtpSchema\Parser\SchemaLoader;
7use SocialDept\AtpSchema\Validation\LexiconValidator;
8
9class ValidateCommand extends Command
10{
11 /**
12 * The name and signature of the console command.
13 */
14 protected $signature = 'schema:validate
15 {nsid : The NSID of the schema to validate}
16 {--data= : JSON data to validate against the schema}
17 {--file= : Path to file containing JSON data to validate}';
18
19 /**
20 * The console command description.
21 */
22 protected $description = 'Validate data against ATProto Lexicon schemas';
23
24 /**
25 * Execute the console command.
26 */
27 public function handle(): int
28 {
29 $nsid = $this->argument('nsid');
30 $dataJson = $this->option('data');
31 $dataFile = $this->option('file');
32
33 if (! $dataJson && ! $dataFile) {
34 $this->error('Either --data or --file option must be provided');
35
36 return self::FAILURE;
37 }
38
39 try {
40 // Load data
41 if ($dataFile) {
42 if (! file_exists($dataFile)) {
43 $this->error("File not found: {$dataFile}");
44
45 return self::FAILURE;
46 }
47
48 $dataJson = file_get_contents($dataFile);
49 }
50
51 $data = json_decode($dataJson, true);
52
53 if (json_last_error() !== JSON_ERROR_NONE) {
54 $this->error('Invalid JSON: '.json_last_error_msg());
55
56 return self::FAILURE;
57 }
58
59 // Load schema and validate
60 $sources = config('schema.sources', []);
61 $loader = new SchemaLoader($sources);
62 $validator = new LexiconValidator($loader);
63
64 $this->info("Validating data against schema: {$nsid}");
65
66 $document = $loader->load($nsid);
67
68 $errors = $validator->validateWithErrors($data, $document);
69
70 if (empty($errors)) {
71 $this->info('✓ Validation passed');
72
73 return self::SUCCESS;
74 }
75
76 $this->error('✗ Validation failed:');
77 $this->newLine();
78
79 foreach ($errors as $field => $fieldErrors) {
80 $this->line(" {$field}:");
81 foreach ($fieldErrors as $error) {
82 $this->line(" - {$error}");
83 }
84 }
85
86 return self::FAILURE;
87 } catch (\Exception $e) {
88 $this->error('Validation error: '.$e->getMessage());
89
90 if ($this->output->isVerbose()) {
91 $this->error($e->getTraceAsString());
92 }
93
94 return self::FAILURE;
95 }
96 }
97}