Skip to content

Commit 8aa1272

Browse files
committed
API spec modify existing file
1 parent f80a652 commit 8aa1272

File tree

2 files changed

+83
-10
lines changed

2 files changed

+83
-10
lines changed

batch/apispec.php

Lines changed: 73 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
22
// apispec.php -- HotCRP script for generating OpenAPI specification
3-
// Copyright (c) 2006-2022 Eddie Kohler; see LICENSE.
3+
// Copyright (c) 2006-2024 Eddie Kohler; see LICENSE.
44

55
if (realpath($_SERVER["PHP_SELF"]) === __FILE__) {
66
require_once(dirname(__DIR__) . "/src/init.php");
@@ -22,12 +22,29 @@ class APISpec_Batch {
2222
private $schemas;
2323
/** @var ?object */
2424
private $parameters;
25+
/** @var string */
26+
private $output_file = "-";
2527

2628
function __construct(Conf $conf, $arg) {
2729
$this->conf = $conf;
2830
$this->user = $conf->root_user();
2931
$this->api_map = $conf->api_map();
3032
$this->j = (object) [];
33+
34+
if (isset($arg["i"])) {
35+
if ($arg["i"] === "-") {
36+
$s = stream_get_contents(STDIN);
37+
} else {
38+
$s = file_get_contents_throw(safe_filename($arg["i"]));
39+
}
40+
if ($s === false || !is_object($this->j = json_decode($s))) {
41+
throw new CommandLineException($arg["i"] . ": Invalid input");
42+
}
43+
$this->output_file = $arg["i"];
44+
}
45+
if (isset($arg["o"])) {
46+
$this->output_file = $arg["o"];
47+
}
3148
}
3249

3350
/** @return int */
@@ -52,7 +69,18 @@ function run() {
5269
}
5370
}
5471

55-
fwrite(STDOUT, json_encode($this->j, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n");
72+
if (($this->output_file ?? "-") === "-") {
73+
$out = STDOUT;
74+
} else {
75+
$out = @fopen(safe_filename($this->output_file), "wb");
76+
if (!$out) {
77+
throw error_get_last_as_exception("{$this->output_file}: ");
78+
}
79+
}
80+
fwrite($out, json_encode($this->j, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n");
81+
if ($out !== STDOUT) {
82+
fclose($out);
83+
}
5684
return 0;
5785
}
5886

@@ -124,7 +152,7 @@ private function expand_path_method($path, $method, $known, $j) {
124152
$pathj = $this->paths->$path = $this->paths->$path ?? (object) [];
125153
$lmethod = strtolower($method);
126154
$xj = $pathj->$lmethod = $pathj->$lmethod ?? (object) [];
127-
$this->expand_request($xj, $known, $j);
155+
$this->expand_request($xj, $known, $j, "{$path}.{$lmethod}");
128156
$this->expand_response($xj, $j);
129157
}
130158

@@ -199,19 +227,20 @@ private function resolve_common_param($name) {
199227

200228
/** @param object $x
201229
* @param array<string,int> $known
202-
* @param object $j */
203-
private function expand_request($x, $known, $j) {
230+
* @param object $j
231+
* @param string $path */
232+
private function expand_request($x, $known, $j, $path) {
204233
$params = $body_properties = $body_required = [];
205234
$has_file = false;
206235
foreach ($known as $name => $f) {
207236
if ($name === "*") {
208237
// skip
209238
} else if ($name === "p" && $f === (self::F_REQUIRED | self::F_PATH)) {
210-
$params[] = $this->resolve_common_param("p");
239+
$params["p"] = $this->resolve_common_param("p");
211240
} else if ($name === "redirect" && $f === 0) {
212-
$params[] = $this->resolve_common_param("redirect");
241+
$params["redirect"] = $this->resolve_common_param("redirect");
213242
} else if (($f & (self::F_BODY | self::F_FILE)) === 0) {
214-
$params[] = (object) [
243+
$params[$name] = (object) [
215244
"name" => $name,
216245
"in" => "query",
217246
"required" => ($f & self::F_REQUIRED) !== 0,
@@ -230,7 +259,39 @@ private function expand_request($x, $known, $j) {
230259
}
231260
}
232261
if (!empty($params)) {
233-
$x->parameters = $params;
262+
$x->parameters = $x->parameters ?? [];
263+
$xparams = [];
264+
foreach ($x->parameters as $i => $pj) {
265+
if (isset($pj->name) && is_string($pj->name)) {
266+
$xparams[$pj->name] = $i;
267+
} else if (isset($pj->{"\$ref"}) && is_string($pj->{"\$ref"})
268+
&& preg_match('/\A\#\/components\/parameters\/([^+]*)/', $pj->{"\$ref"}, $m)) {
269+
$xparams[$m[1]] = $i;
270+
}
271+
}
272+
foreach ($params as $n => $npj) {
273+
$i = $xparams[$n] ?? null;
274+
if ($i === null) {
275+
$x->parameters[] = $npj;
276+
continue;
277+
}
278+
$xpj = $x->parameters[$i];
279+
if (isset($xpj->{"\$ref"}) !== isset($npj->{"\$ref"})) {
280+
fwrite(STDERR, "{$path}.param[{$n}]: \$ref status differs\n");
281+
} else if (isset($xpj->{"\$ref"})) {
282+
if ($xpj->{"\$ref"} !== $npj->{"\$ref"}) {
283+
fwrite(STDERR, "{$path}.param[{$n}]: \$ref destination differs\n");
284+
}
285+
} else {
286+
foreach ((array) $npj as $k => $v) {
287+
if (!isset($xpj->$k)) {
288+
$xpj->$k = $v;
289+
} else if (is_scalar($v) && $xpj->$k !== $v) {
290+
fwrite(STDERR, "{$path}.param[{$n}]: {$k} differs\n");
291+
}
292+
}
293+
}
294+
}
234295
}
235296
if (!empty($body_properties)) {
236297
$schema = (object) [
@@ -303,7 +364,9 @@ static function make_args($argv) {
303364
$arg = (new Getopt)->long(
304365
"name:,n: !",
305366
"config: !",
306-
"help,h !"
367+
"help,h !",
368+
"i:,input: =FILE Modify existing specification in FILE",
369+
"o:,output: =FILE Write specification to FILE"
307370
)->description("Generate an OpenAPI specification.
308371
Usage: php batch/apispec.php")
309372
->helpopt("help")

lib/base.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,16 @@ function tempdir($mode = 0700) {
746746
}
747747

748748

749+
/** @param string $filename
750+
* @return string */
751+
function safe_filename($filename) {
752+
$colon = strpos($filename, ":");
753+
if ($colon !== false && ctype_alnum(substr($filename, 0, $colon))) {
754+
return "file://{$filename}";
755+
}
756+
return $filename;
757+
}
758+
749759
/** @return Exception */
750760
function error_get_last_as_exception($prefix) {
751761
$msg = preg_replace('/.*: /', "", error_get_last()["message"]);

0 commit comments

Comments
 (0)