66use App \Service \DOMJudgeService ;
77use App \Utils \Utils ;
88use Doctrine \ORM \EntityManagerInterface ;
9+ use Symfony \Component \Console \Attribute \Argument ;
910use Symfony \Component \Console \Attribute \AsCommand ;
11+ use Symfony \Component \Console \Attribute \Option ;
1012use Symfony \Component \Console \Command \Command ;
11- use Symfony \Component \Console \Input \InputArgument ;
1213use Symfony \Component \Console \Input \InputInterface ;
13- use Symfony \Component \Console \Input \InputOption ;
1414use Symfony \Component \Console \Output \OutputInterface ;
1515use Symfony \Component \HttpFoundation \File \UploadedFile ;
1616use Symfony \Component \HttpFoundation \Request ;
2020 name: 'api:call ' ,
2121 description: 'Call the DOMjudge API directly. Note: this will use admin credentials '
2222)]
23- class CallApiActionCommand extends Command
23+ readonly class CallApiActionCommand
2424{
25- public function __construct (protected readonly DOMJudgeService $ dj , protected readonly EntityManagerInterface $ em )
26- {
27- parent ::__construct ();
28- }
29-
30- protected function configure (): void
31- {
32- $ this
33- ->addArgument (
34- 'endpoint ' ,
35- InputArgument::REQUIRED ,
36- 'The API endpoint to call. For example `contests/3/teams` '
37- )
38- ->addOption (
39- 'method ' ,
40- 'm ' ,
41- InputOption::VALUE_REQUIRED ,
42- 'The HTTP method to use ' ,
43- Request::METHOD_GET
44- )
45- ->addOption (
46- 'data ' ,
47- 'd ' ,
48- InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY ,
49- 'POST data to use as key=value. Only allowed when the method is POST or PUT '
50- )
51- ->addOption (
52- 'json ' ,
53- 'j ' ,
54- InputOption::VALUE_REQUIRED ,
55- 'JSON body data to use. Only allowed when the method is POST or PUT '
56- )
57- ->addOption (
58- 'file ' ,
59- 'f ' ,
60- InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY ,
61- 'Files to use as field=filename. Only allowed when the method is POST or PUT '
62- )
63- ->addOption (
64- 'user ' ,
65- 'u ' ,
66- InputOption::VALUE_REQUIRED ,
67- 'User to use for API requests. If not given, the first admin user will be used '
68- );
69- }
25+ public function __construct (
26+ protected DOMJudgeService $ dj ,
27+ protected EntityManagerInterface $ em
28+ ) {}
7029
71- protected function execute (InputInterface $ input , OutputInterface $ output ): int
72- {
73- if (!in_array ($ input ->getOption ('method ' ), [Request::METHOD_GET , Request::METHOD_POST , Request::METHOD_PUT ], true )) {
30+ /**
31+ * @param array<string, mixed> $data
32+ * @param array<string, string> $file
33+ */
34+ public function __invoke (
35+ #[Argument(description: 'The API endpoint to call. For example `contests/3/teams` ' )]
36+ string $ endpoint ,
37+ InputInterface $ input ,
38+ OutputInterface $ output ,
39+ #[Option(description: 'POST data to use as key=value. Only allowed when the method is POST or PUT ' , shortcut: 'd ' )]
40+ array $ data = [],
41+ #[Option(description: 'Files to use as field=filename. Only allowed when the method is POST or PUT ' , shortcut: 'f ' )]
42+ array $ file = [],
43+ #[Option(description: 'User to use for API requests. If not given, the first admin user will be used ' , shortcut: 'u ' )]
44+ ?string $ user = null ,
45+ #[Option(description: 'JSON body data to use. Only allowed when the method is POST or PUT ' , shortcut: 'j ' )]
46+ ?string $ json = null ,
47+ #[Option(description: 'The HTTP method to use ' , shortcut: 'm ' )]
48+ string $ method = Request::METHOD_GET ,
49+ ): int {
50+ if (!in_array ($ method , [Request::METHOD_GET , Request::METHOD_POST , Request::METHOD_PUT ], true )) {
7451 $ output ->writeln ('Error: only GET, POST and PUT methods are supported ' );
7552 return Command::FAILURE ;
7653 }
7754
78- if ($ input -> getOption ( ' user ' ) ) {
55+ if ($ user ) {
7956 $ user = $ this ->em
8057 ->getRepository (User::class)
81- ->findOneBy (['username ' => $ input -> getOption ( ' user ' ) ]);
58+ ->findOneBy (['username ' => $ user ]);
8259 if (!$ user ) {
8360 $ output ->writeln ('Error: Provided user not found ' );
8461 return Command::FAILURE ;
@@ -101,52 +78,52 @@ protected function execute(InputInterface $input, OutputInterface $output): int
10178 }
10279 }
10380
104- $ data = [];
81+ $ jsonData = [];
10582 $ files = [];
106- if (in_array ($ input -> getOption ( ' method ' ) , [Request::METHOD_POST , Request::METHOD_PUT ], true )) {
107- foreach ($ input -> getOption ( ' data ' ) as $ dataItem ) {
83+ if (in_array ($ method , [Request::METHOD_POST , Request::METHOD_PUT ], true )) {
84+ foreach ($ data as $ dataItem ) {
10885 $ parts = explode ('= ' , $ dataItem , 2 );
10986 if (count ($ parts ) !== 2 ) {
11087 $ output ->writeln (sprintf ('Error: data item %s is not in key=value format ' , $ dataItem ));
111- return self ::FAILURE ;
88+ return Command ::FAILURE ;
11289 }
11390
114- $ data [$ parts [0 ]] = $ parts [1 ];
91+ $ jsonData [$ parts [0 ]] = $ parts [1 ];
11592 }
11693
117- if ($ json = $ input -> getOption ( ' json ' ) ) {
118- $ data = array_merge ($ data , Utils::jsonDecode ($ json ));
94+ if ($ json ) {
95+ $ jsonData = array_merge ($ jsonData , Utils::jsonDecode ($ json ));
11996 }
12097
121- foreach ($ input -> getOption ( ' file ' ) as $ fileItem ) {
98+ foreach ($ file as $ fileItem ) {
12299 $ parts = explode ('= ' , $ fileItem , 2 );
123100 if (count ($ parts ) !== 2 ) {
124101 $ output ->writeln (sprintf ('Error: file item %s is not in key=value format ' , $ fileItem ));
125- return self ::FAILURE ;
102+ return Command ::FAILURE ;
126103 }
127104
128105 if (!file_exists ($ parts [1 ])) {
129106 $ output ->writeln (sprintf ('Error: file %s does not exist ' , $ parts [1 ]));
130- return self ::FAILURE ;
107+ return Command ::FAILURE ;
131108 }
132109
133110 $ files [$ parts [0 ]] = new UploadedFile ($ parts [1 ], basename ($ parts [1 ]), mime_content_type ($ parts [1 ]), null , true );
134111 }
135112 } else {
136- if ($ input -> getOption ( ' data ' ) ) {
113+ if ($ data ) {
137114 $ output ->writeln ('Error: data not allowed for GET methods. ' );
138115 return Command::FAILURE ;
139116 }
140- if ($ input -> getOption ( ' file ' ) ) {
117+ if ($ file ) {
141118 $ output ->writeln ('Error: files not allowed for GET methods. ' );
142119 return Command::FAILURE ;
143120 }
144121 }
145122
146123 try {
147124 $ response = '' ;
148- $ this ->dj ->withAllRoles (function () use ($ input , $ data , $ files , &$ response ) {
149- $ response = $ this ->dj ->internalApiRequest ('/ ' . $ input -> getArgument ( ' endpoint ' ) , $ input -> getOption ( ' method ' ) , $ data , $ files , true );
125+ $ this ->dj ->withAllRoles (function () use ($ method , $ endpoint , $ jsonData , $ files , &$ response ): void {
126+ $ response = $ this ->dj ->internalApiRequest ('/ ' . $ endpoint , $ method , $ jsonData , $ files , true );
150127 }, $ user );
151128 } catch (HttpException $ e ) {
152129 $ output ->writeln ($ e ->getMessage ());
0 commit comments