Skip to content

Commit

Permalink
Merge branch 'master' into location-required-props
Browse files Browse the repository at this point in the history
  • Loading branch information
niemela authored Apr 11, 2023
2 parents 3ee46ba + a4c31d3 commit fcb6f47
Show file tree
Hide file tree
Showing 42 changed files with 1,711 additions and 8 deletions.
14 changes: 8 additions & 6 deletions Contest_API.md
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,7 @@ All event types correspond to an API endpoint, as specified in the table below.
| runs | `/contests/<id>/runs/<id>`
| clarifications | `/contests/<id>/clarifications/<id>`
| awards | `/contests/<id>/awards/<id>`
| commentary | `/contests/<id>/commentary/<id>`

Each event is a notification that an object or a collection has changed
(and hence the contents of the corresponding endpoint) to `data`.
Expand Down Expand Up @@ -774,12 +775,12 @@ Clients with the `contest_thaw` [capability](#capabilities) have the ability to
set a time when the contest will be thawed via a PATCH method.

The PATCH must include a valid JSON object with two properties:
the contest `id` (used for verification) and a `contest_thaw_time`, a `<TIME>` value.
the contest `id` (used for verification) and a `scoreboard_thaw_time`, a `<TIME>` value.

The request should succeed with a 204 response code with no body if the server changed the
thaw time to the time specified.

The server may also thaw the contest at the current server time if the provided `contest_thaw_time`
The server may also thaw the contest at the current server time if the provided `scoreboard_thaw_time`
is in the past. In that case the server must reply with a 200 response code and the modified contest
as body, so the client knows the server used a different thaw time.

Expand Down Expand Up @@ -866,7 +867,7 @@ Request data:
```json
{
"id": "wf2014",
"contest_thaw_time": "2014-06-25T19:30:00+01"
"scoreboard_thaw_time": "2014-06-25T19:30:00+01"
}
```

Expand Down Expand Up @@ -1274,7 +1275,7 @@ The following endpoints are associated with teams:
| Endpoint | Mime-type | Description
| :--------------------------- | :--------------- | :----------
| `/contests/<id>/teams` | application/json | JSON array of all teams with properties as specified by `/access`.
| `/contests/<id>/teams/<id>` | application/json | JSON object representing a single team with properties as specified by `/access`.
| `/contests/<id>/teams/<id>` | application/json | JSON object representing a single team with properties as specified by `/access`.

Properties of team objects:

Expand All @@ -1283,6 +1284,7 @@ Properties of team objects:
| id | ID | Identifier of the team. Usable as a label, at WFs normally the team seat number.
| icpc\_id | string ? | External identifier from ICPC CMS.
| name | string | Name of the team.
| label | string | Label of the team, at WFs normally the team seat number.
| display\_name | string ? | Display name of the team. If not set, a client should revert to using the name instead.
| organization\_id | ID ? | Identifier of the [ organization](#organizations) (e.g. university or other entity) that this team is affiliated to.
| group\_ids | array of ID ? | Identifiers of the [ group(s)](#groups) this team is part of (at ICPC WFs these are the super-regions). No meaning must be implied or inferred from the order of IDs. The array may be empty. Required iff groups endpoint is available.
Expand Down Expand Up @@ -1314,8 +1316,8 @@ Request:
Returned data:

```json
[{"id":"11","icpc_id":"201433","name":"Shanghai Tigers","organization_id":"inst123","group_ids":["asia-74324325532"]},
{"id":"123","name":"CMU1","organization_id":"inst105","group_ids":["8","11"]}
[{"id":"team11","icpc_id":"201433","label":"11","name":"Shanghai Tigers","organization_id":"inst123","group_ids":["asia-74324325532"]},
{"id":"team123","label":"123","name":"CMU1","organization_id":"inst105","group_ids":["8","11"]}
]
```

Expand Down
4 changes: 2 additions & 2 deletions Contest_Control_System_Requirements.md
Original file line number Diff line number Diff line change
Expand Up @@ -955,8 +955,8 @@ return from the `/access` endpoint:
{
"type": "teams",
"properties": [
"id", "icpc_id", "name", "display_name", "organization_id",
"group_ids"
"id", "icpc_id", "label", "name", "display_name",
"organization_id", "group_ids"
]
},
{
Expand Down
147 changes: 147 additions & 0 deletions check-api-consistency.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
#!/usr/bin/env php
<?php
/**
* This is a helper script to check-api to verify that the contents of
* the REST endpoints and the event feed are consistent. It is not
* meant to be executed independently.
*/

$dir = $argv[1];

$endpoints = array_splice($argv, 2);

$feed_json = json_decode(file_get_contents($dir.'/event-feed.json'), true);

$debug = getenv('DEBUG');
$strict = getenv('STRICT');

$errors = 0;
$warnings = 0;

function error($msg)
{
global $errors;

$errors++;
echo "Error: $msg\n";
}

function warning($msg)
{
global $warnings;

$warnings++;
echo "Warning: $msg\n";
}

foreach ($feed_json as $row) {
$endpoint = $row['type'];
if ($endpoint === 'contest') {
// New feed format uses singular for contest
$endpoint = 'contests';
}
$id = isset($row['data']['id']) ? $row['data']['id'] : '_single_';
$feed_data[$endpoint][$id][] = $row;
}

// Given two associative arrays, return the keys of their symmetric difference.
function array_diff_keys($a, $b)
{
$keys = array_unique(array_merge(array_keys($a), array_keys($b)));
$diff = [];
foreach ($keys as $key) {
if ((array_key_exists($key, $a) xor array_key_exists($key, $b)) ||
(array_key_exists($key, $a) and ($a[$key]!==$b[$key]))) {
$diff[] = $key;
}
}
return $diff;
}

// Let's assume and check that each element ID gets created and deleted at most once.
foreach ($feed_data as $endpoint => $elements) {
if ($endpoint==='state') {
foreach ($elements as $id => $rows) {
if ($id!=='_single_') {
error("'state' cannot have ID '$id' set.");
}
for ($i=0; $i<count($rows); $i++) {
if (isset($rows[$i]['op']) && $rows[$i]['op']!=='update') {
error("'state' operation '$rows[$i][op]' not allowed.");
} elseif (!isset($rows[$i]['data'])) { // isset also checks for null
error("'state' can not be deleted.");
}
}
}
} else {
foreach ($elements as $id => $rows) {
if (isset($rows[0]['op']) && $rows[0]['op']!=='create') {
error("'$endpoint/$id' not created first.");
}
for ($i=1; $i<count($rows); $i++) {
if (isset($rows[$i]['op'])) {
switch ($rows[$i]['op']) {
case 'create':
warning("'$endpoint/$id' created again.");
break;
case 'update':
break;
case 'delete':
if ($i<count($rows)-1) {
error("'$endpoint/$id' deleted before last change.");
}
break;
default:
error("'$endpoint/$id' unknown operation '$rows[$i][op]'.");
}
} elseif (!isset($rows[$i]['data']) && $i<count($rows)-1) {
if ($i<count($rows)-1) {
error("'$endpoint/$id' deleted before last change.");
}
break;
}
}
}
}
}

// Now check that each REST endpoint element appears in the feed.
foreach ($endpoints as $endpoint) {
$endpoint_json = json_decode(file_get_contents($dir.'/'.$endpoint.'.json'), true);
if (in_array($endpoint,['contests','state'])) $endpoint_json = [$endpoint_json];
foreach ($endpoint_json as $element) {
$id = isset($element['id']) ? $element['id'] : '_single_';
$endpoint_data[$endpoint][$id] = $element;
if (!isset($feed_data[$endpoint][$id])) {
error("'$endpoint".($id==='_single_' ? '' : "/$id")."' not found in feed.");
if ($debug) var_dump($element);
}
}
}

// Finally check that each non-deleted item from the feed exists in
// its REST endpoint and has equal contents to its last feed entry.
foreach ($feed_data as $endpoint => $elements) {
foreach ($elements as $id => $rows) {
$last = end($rows);
if ((isset($last['op']) && $last['op']!=='delete') || (!isset($last['op']) && isset($last['data'])) ) {
if (!isset($endpoint_data[$endpoint][$id])) {
error("'$endpoint".($id==='_single_' ? '' : "/$id")."' not found in REST endpoint.");
} elseif ($last['data']!==$endpoint_data[$endpoint][$id]) {
$diff = array_diff_keys($last['data'], $endpoint_data[$endpoint][$id]);
warning("'$endpoint".($id==='_single_' ? '' : "/$id")."' data mismatch between feed and REST endpoint: ".implode(',', $diff));
if ($debug) var_dump($last['data'], $endpoint_data[$endpoint][$id]);
}
}
}
}

if ($errors>0) {
echo "Found $errors errors and $warnings warnings.\n";
exit(1);
}

if ($warnings>0) {
echo "Found $warnings warnings.\n";
if ($strict) exit(1);
}
Loading

0 comments on commit fcb6f47

Please sign in to comment.