-
Notifications
You must be signed in to change notification settings - Fork 10
/
check-api-consistency.php
executable file
·149 lines (133 loc) · 5.01 KB
/
check-api-consistency.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#!/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) {
# TODO: decide whether access should be in the feed:
if ($endpoint==='access') continue;
$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);
}