Skip to content

Commit

Permalink
API spec; rename dryrun parameter to dry_run
Browse files Browse the repository at this point in the history
  • Loading branch information
kohler committed Dec 6, 2024
1 parent fb0c976 commit 0c1d631
Show file tree
Hide file tree
Showing 12 changed files with 194 additions and 95 deletions.
34 changes: 28 additions & 6 deletions devel/apidoc/submissions.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,27 @@ A request with a `p` parameter (as a path parameter `/{p}/paper` or a query
parameter) modifies the submission with that ID. The special ID `new` can be
used to create a submission.

The body of the request may be formatted as an HTML form
(`application/x-www-form-urlencoded` or `multipart/form-data`), a JSON object
(`application/json`), or a ZIP file (`application/zip`—see below). HTML form
input follows the conventions of the HotCRP web application and is subject to
change at any time.
Modifications are specified using a JSON object. There are three ways to
provide that JSON, depending on the content-type of the request:

1. As a request body with content-type `application/json`.
2. As a file named `data.json` in an uploaded ZIP archive, with content-type
`application/zip`.
3. As a parameter named `json` (body type
`application/x-www-form-urlencoded` or `multipart/form-data`).

The JSON upload must be formatted as an object.

ZIP and form uploads also support document upload. A document is referenced
via `content_file` fields in the JSON.

### Multiple submissions

A request with no `p` parameter can create or modify any number of
submissions.
submissions. Upload types are the same as for single submissions, but the JSON
upload is defined as an array of objects. These objects are processed in turn.

Currently, multiple-submission upload is only allowed for administrators.

### ZIP uploads

Expand All @@ -66,3 +77,14 @@ $ cat data.json
$ zip upload.zip data.json paper.pdf
$ curl -H "Authorization: bearer hct_XXX" --data-binary @upload.zip -H "Content-Type: application/zip" SITEURL/api/paper
```
### Parameters

Set `dry_run=1` to check the upload for errors without modifying the
database.

Three additional parameters are available to administrators. Set
`disable_users=1` to disable newly-created users; set `add_topics=1` to
automatically add newly-referenced topics; and set `notify=0` to make changes
without notifying contacts.

### Responses
12 changes: 8 additions & 4 deletions etc/apiexpansions.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
},
{
"name": "paper", "post": true, "merge": true,
"tags": ["Submissions"], "order": 0
"tags": ["Submissions"], "order": 0,
"response": "?paper ?papers ?+change_list ?+change_lists ?+valid",
"response_info": {"paper": "paper", "papers": "[paper]",
"change_list": "[string]", "change_lists": "[[string]]",
"valid": "boolean", "dry_run": "boolean"
}
},
{
"name": "formatcheck", "get": true, "merge": true,
Expand Down Expand Up @@ -167,8 +172,7 @@
"name": "comment", "get": true, "merge": true,
"tags": ["Comments"], "order": 0,
"parameter_info": {"content": {"type": "boolean", "description": "False omits comment content from response"}},
"response_info": {"comment": {"$ref": "#/components/schemas/comment"},
"comments": {"type": "list", "items": {"$ref": "#/components/schemas/comment"}}}
"response_info": {"comment": "comment", "comments": "[comment]"}
},
{
"name": "comment", "post": true, "merge": true,
Expand All @@ -185,7 +189,7 @@
"blind": {"type": "boolean"},
"by_author": {"type": "boolean"},
"review_token": {"type": "string"}},
"response_info": {"comment": {"$ref": "#/components/schemas/comment"}}
"response_info": {"comment": "comment"}
},
{
"name": "mentioncompletion", "get": true, "merge": true,
Expand Down
10 changes: 5 additions & 5 deletions etc/apifunctions.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@
{
"name": "fieldhtml", "get": true,
"function": "Search_API::fieldhtml",
"parameters": "f ?aufull ?session q ?t ?sort ?qt ?reviewer",
"parameters": "f ?aufull ?session q ?t ?qt ?reviewer ?sort ?scoresort",
"response": "fields data ?stat ?classes ?attr"
},
{
Expand Down Expand Up @@ -188,9 +188,9 @@
"response": "?paper ?papers"
},
{
"name": "paper", "post": true, "allow_if": "chair",
"name": "paper", "post": true,
"function": "Paper_API::run",
"parameters": "?p ?dryrun"
"parameters": "?p ?dry_run ?disable_users ?add_topics ?notify"
},
{
"name": "pc", "get": true,
Expand Down Expand Up @@ -300,8 +300,8 @@
{
"name": "settings", "get": true, "post": true,
"function": "Settings_API::run",
"parameters": "?=settings ?dryrun",
"response": "?dry_run ?changes"
"parameters": "?=settings ?+dry_run ?+reset ?+filename",
"response": "?+dry_run ?+change_list"
},
{
"name": "shepherd", "get": true, "paper": true,
Expand Down
103 changes: 76 additions & 27 deletions etc/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,46 @@
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/minimal_response"
"allOf": [
{
"$ref": "#/components/schemas/minimal_response"
},
{
"type": "object",
"properties": {
"paper": {
"$ref": "#/components/schemas/paper"
},
"papers": {
"type": "array",
"items": {
"$ref": "#/components/schemas/paper"
}
},
"change_list": {
"type": "array",
"items": {
"type": "string"
}
},
"change_lists": {
"type": "array",
"items": {
"type": "array",
"items": {
"type": "string"
}
}
},
"valid": {
"type": "array",
"items": {
"type": "boolean"
}
}
}
}
]
}
}
}
Expand All @@ -112,14 +151,32 @@
"$ref": "#/components/parameters/p.opt"
},
{
"name": "dryrun",
"name": "dry_run",
"in": "query",
"required": false,
"schema": {}
},
{
"name": "disable_users",
"in": "query",
"required": false,
"schema": {}
},
{
"name": "add_topics",
"in": "query",
"required": false,
"schema": {}
},
{
"name": "notify",
"in": "query",
"required": false,
"schema": {}
}
],
"summary": "Create or modify submission(s)",
"description": "### Single submission\n\nA request with a `p` parameter (as a path parameter `/{p}/paper` or a query\nparameter) modifies the submission with that ID. The special ID `new` can be\nused to create a submission.\n\nThe body of the request may be formatted as an HTML form\n(`application/x-www-form-urlencoded` or `multipart/form-data`), a JSON object\n(`application/json`), or a ZIP file (`application/zip`—see below). HTML form\ninput follows the conventions of the HotCRP web application and is subject to\nchange at any time.\n\n### Multiple submissions\n\nA request with no `p` parameter can create or modify any number of\nsubmissions.\n\n### ZIP uploads\n\nA ZIP upload should contain a file named `data.json` (`PREFIX-data.json` is\nalso acceptable). This file’s content is parsed as JSON and treated a\nsubmission object (or array of submission objects). Attachment fields in the\nJSON content can refer to other files in the ZIP. For instance, this shell\nsession might upload a submission with content `paper.pdf`:\n\n```\n$ cat data.json\n{\n\t\"object\": \"paper\",\n\t\"pid\": \"new\",\n\t\"title\": \"Aught: A Methodology for the Visualization of Scheme\",\n\t\"authors\": [{\"name\": \"Nevaeh Gomez\", \"email\": \"[email protected]\"}],\n\t\"submission\": {\"content_file\": \"paper.pdf\"},\n\t\"status\": \"submitted\"\n}\n$ zip upload.zip data.json paper.pdf\n$ curl -H \"Authorization: bearer hct_XXX\" --data-binary @upload.zip -H \"Content-Type: application/zip\" SITEURL/api/paper\n```\n"
"description": "### Single submission\n\nA request with a `p` parameter (as a path parameter `/{p}/paper` or a query\nparameter) modifies the submission with that ID. The special ID `new` can be\nused to create a submission.\n\nModifications are specified using a JSON object. There are three ways to\nprovide that JSON, depending on the content-type of the request:\n\n1. As a request body with content-type `application/json`.\n2. As a file named `data.json` in an uploaded ZIP archive, with content-type\n `application/zip`.\n3. As a parameter named `json` (body type\n `application/x-www-form-urlencoded` or `multipart/form-data`).\n\nThe JSON upload must be formatted as an object.\n\nZIP and form uploads also support document upload. A document is referenced\nvia `content_file` fields in the JSON.\n\n### Multiple submissions\n\nA request with no `p` parameter can create or modify any number of\nsubmissions. Upload types are the same as for single submissions, but the JSON\nupload is defined as an array of objects. These objects are processed in turn.\n\nCurrently, multiple-submission upload is only allowed for administrators.\n\n### ZIP uploads\n\nA ZIP upload should contain a file named `data.json` (`PREFIX-data.json` is\nalso acceptable). This file’s content is parsed as JSON and treated a\nsubmission object (or array of submission objects). Attachment fields in the\nJSON content can refer to other files in the ZIP. For instance, this shell\nsession might upload a submission with content `paper.pdf`:\n\n```\n$ cat data.json\n{\n\t\"object\": \"paper\",\n\t\"pid\": \"new\",\n\t\"title\": \"Aught: A Methodology for the Visualization of Scheme\",\n\t\"authors\": [{\"name\": \"Nevaeh Gomez\", \"email\": \"[email protected]\"}],\n\t\"submission\": {\"content_file\": \"paper.pdf\"},\n\t\"status\": \"submitted\"\n}\n$ zip upload.zip data.json paper.pdf\n$ curl -H \"Authorization: bearer hct_XXX\" --data-binary @upload.zip -H \"Content-Type: application/zip\" SITEURL/api/paper\n```\n### Parameters\n\nSet `dry_run=1` to check the upload for errors without modifying the\ndatabase.\n\nThree additional parameters are available to administrators. Set\n`disable_users=1` to disable newly-created users; set `add_topics=1` to\nautomatically add newly-referenced topics; and set `notify=0` to make changes\nwithout notifying contacts.\n\n### Responses\n"
}
},
"/formatcheck": {
Expand Down Expand Up @@ -1003,7 +1060,8 @@
}
}
}
}
},
"summary": "Retrieve search results"
}
},
"/fieldhtml": {
Expand Down Expand Up @@ -3557,27 +3615,6 @@
"tags": [
"Settings"
],
"parameters": [
{
"name": "dryrun",
"in": "query",
"required": false,
"schema": {}
}
],
"requestBody": {
"description": "",
"content": {
"application/x-www-form-urlencoded": {
"schema": {
"type": "object",
"properties": {
"settings": {}
}
}
}
}
},
"responses": {
"200": {
"description": "",
Expand Down Expand Up @@ -3618,7 +3655,19 @@
],
"parameters": [
{
"name": "dryrun",
"name": "dry_run",
"in": "query",
"required": false,
"schema": {}
},
{
"name": "reset",
"in": "query",
"required": false,
"schema": {}
},
{
"name": "filename",
"in": "query",
"required": false,
"schema": {}
Expand Down Expand Up @@ -3651,7 +3700,7 @@
"type": "object",
"properties": {
"dry_run": {},
"changes": {}
"change_list": {}
}
}
]
Expand Down
8 changes: 5 additions & 3 deletions lib/messageset.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class MessageItem implements JsonSerializable {
public $pos2;
/** @var ?string */
public $context;
/** @var ?string */
/** @var null|int|string */
public $landmark;

/** @param ?string $field
Expand Down Expand Up @@ -103,20 +103,22 @@ function with_prefix($text) {

#[\ReturnTypeWillChange]
function jsonSerialize() {
$x = [];
$x = ["status" => $this->status];
if ($this->field !== null) {
$x["field"] = $this->field;
}
if ($this->message !== "") {
$x["message"] = $this->message;
}
$x["status"] = $this->status;
if ($this->pos1 !== null && $this->context !== null) {
$x["context"] = Ht::make_mark_substring($this->context, $this->pos1, $this->pos2);
} else if ($this->pos1 !== null) {
$x["pos1"] = $this->pos1;
$x["pos2"] = $this->pos2;
}
if ($this->landmark !== null) {
$x["landmark"] = $this->landmark;
}
return (object) $x;
}

Expand Down
23 changes: 13 additions & 10 deletions src/api/api_paper.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,18 +95,18 @@ private function run_post(Qrequest $qreq, ?PaperInfo $prow) {

// set parameters
if ($this->user->privChair) {
if (friendly_boolean($qreq->disableusers)) {
if (friendly_boolean($qreq->disable_users)) {
$this->disable_users = true;
}
if (friendly_boolean($qreq->notify) === false) {
$this->notify = false;
}
if (friendly_boolean($qreq->addtopics)) {
if (friendly_boolean($qreq->add_topics)) {
$this->conf->topic_set()->set_auto_add(true);
$this->conf->options()->refresh_topics();
}
}
if (friendly_boolean($qreq->dryrun)) {
if (friendly_boolean($qreq->dry_run ?? $qreq->dryrun)) {
$this->dry_run = true;
}

Expand Down Expand Up @@ -234,18 +234,17 @@ private function paper_status() {

/** @param PaperStatus $ps */
private function execute_save($ok, $ps) {
$this->ok = $this->ok && $ok;
if ($this->ok && !$this->dry_run) {
$this->ok = $ok = $ps->execute_save();
if ($ok && !$this->dry_run) {
$ok = $ps->execute_save();
}
foreach ($ps->message_list() as $mi) {
if (!$this->single && $this->landmark) {
if (!$this->single) {
$mi->landmark = $this->landmark;
}
$this->append_item($mi);
}
$this->change_lists[] = $ps->changed_keys();
if ($this->ok && !$this->dry_run) {
if ($ok && !$this->dry_run) {
if ($ps->has_change()) {
$ps->log_save_activity("via API");
}
Expand All @@ -256,6 +255,7 @@ private function execute_save($ok, $ps) {
$this->papers[] = null;
}
$this->valid[] = $ok;
$this->ok = $this->ok && $ok;
}

private function execute_fail() {
Expand All @@ -271,6 +271,9 @@ private function make_result() {
"ok" => $this->ok,
"message_list" => $this->message_list()
]);
if ($this->dry_run) {
$jr->content["dry_run"] = true;
}
if ($this->single) {
$jr->content["change_list"] = $this->change_lists[0];
if ($this->npapers > 0) {
Expand Down Expand Up @@ -318,14 +321,14 @@ static function analyze_json_pid(Conf $conf, $j, $pidflags = 0) {
private function set_json_landmark($index, $jp, $expected = null) {
$pidish = self::analyze_json_pid($this->conf, $jp, 0);
if ($pidish && ($expected === null || $pidish === $expected)) {
$this->landmark = $pidish === "new" ? "index {$index}" : "#{$pidish}";
$this->landmark = $index;
return true;
}
$pidkey = isset($jp->pid) || !isset($jp->id) ? "pid" : "id";
$msg = $pidish ? "<0>ID does not match" : "<0>Format error";
$mi = $this->error_at($pidkey, $msg);
if (!$this->single) {
$mi->landmark = "index {$index}";
$mi->landmark = $index;
}
return false;
}
Expand Down
Loading

0 comments on commit 0c1d631

Please sign in to comment.