Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
46 / 46 |
|
100.00% |
3 / 3 |
CRAP | |
100.00% |
1 / 1 |
ApiQueryTrait | |
100.00% |
46 / 46 |
|
100.00% |
3 / 3 |
11 | |
100.00% |
1 / 1 |
encodeContinuationParameter | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
4 | |||
decodeContinuationParameter | |
100.00% |
16 / 16 |
|
100.00% |
1 / 1 |
4 | |||
getAllowedSortParams | |
100.00% |
22 / 22 |
|
100.00% |
1 / 1 |
3 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\ReadingLists\Api; |
4 | |
5 | use ApiUsageException; |
6 | use MediaWiki\Extension\ReadingLists\ReadingListRepository; |
7 | use Wikimedia\ParamValidator\ParamValidator; |
8 | |
9 | /** |
10 | * Shared sorting / paging for the query APIs. |
11 | * |
12 | * Issue with phan and traits - https://github.com/phan/phan/issues/1067 |
13 | */ |
14 | trait ApiQueryTrait { |
15 | |
16 | // Mode constants, to support different sorting / paging / deleted item behavior for different |
17 | // parameter combinations. For no particular reason, PHP does not allow constants in traits, |
18 | // so we'll use statics instead. |
19 | |
20 | /** |
21 | * Return all lists, or all entries of the specified list(s). |
22 | * Intended for initial copy of data to a new device, or for devices which have information |
23 | * that's too outdated for normal sync. Might also be useful for devices with limited storage |
24 | * capacity, such as web clients. |
25 | * |
26 | * @var string |
27 | */ |
28 | private static $MODE_ALL = 'all'; |
29 | |
30 | /** |
31 | * Return lists/entries which have been changed (or deleted) recently. |
32 | * Intended for syncing updates to a device which has an older snapshot of the data. |
33 | * "Recently" is defined by the changedsince parameter. |
34 | * |
35 | * @var string |
36 | */ |
37 | private static $MODE_CHANGES = 'changes'; |
38 | |
39 | /** |
40 | * Return lists which include a given page. |
41 | * Intended for status indicators and such (e.g. showing a star on the current page if it's |
42 | * included in some list). |
43 | * |
44 | * @var string |
45 | */ |
46 | private static $MODE_PAGE = 'page'; |
47 | |
48 | /** |
49 | * Return lists/entries by ID. |
50 | * Intended for clients which have a limited local caching ability. |
51 | * |
52 | * @var string |
53 | */ |
54 | private static $MODE_ID = 'id'; |
55 | |
56 | /** @var string[] Map of sort keywords used by the API to sort keywords used by the repo. */ |
57 | private static $sortParamMap = [ |
58 | 'name' => ReadingListRepository::SORT_BY_NAME, |
59 | 'updated' => ReadingListRepository::SORT_BY_UPDATED, |
60 | 'ascending' => ReadingListRepository::SORT_DIR_ASC, |
61 | 'descending' => ReadingListRepository::SORT_DIR_DESC, |
62 | ]; |
63 | |
64 | /** |
65 | * Extract continuation data from item position and serialize it into a string. |
66 | * @param array $item Result item to continue from. |
67 | * @param string $mode One of the MODE_* constants. |
68 | * @param string $sort One of the SORT_BY_* constants. |
69 | * @return string |
70 | * @suppress PhanUndeclaredStaticProperty |
71 | */ |
72 | private function encodeContinuationParameter( array $item, $mode, $sort ) { |
73 | if ( $mode === self::$MODE_PAGE ) { |
74 | return $item['id']; |
75 | } elseif ( $sort === ReadingListRepository::SORT_BY_NAME ) { |
76 | if ( self::$prefix === 'rl' ) { |
77 | $name = $item['name']; |
78 | } else { |
79 | $name = $item['title']; |
80 | } |
81 | return $name . '|' . $item['id']; |
82 | } else { |
83 | return $item['updated'] . '|' . $item['id']; |
84 | } |
85 | } |
86 | |
87 | /** |
88 | * Recover continuation data after it has been roundtripped to the client. |
89 | * @param string|null $continue Continuation parameter returned by the client. |
90 | * @param string $mode One of the MODE_* constants. |
91 | * @param string $sort One of the SORT_BY_* constants. |
92 | * @return null|int|string[] |
93 | * - null if there was no continuation parameter; |
94 | * - [ rl(e)_name, rl(e)_id ] for MODE_ALL/MODE_CHANGES when sorting by name; |
95 | * - [ rl(e)_date_updated, rl(e)_id ] for MODE_ALL/MODE_CHANGES when sorting by updated time; |
96 | * - rle_id for MODE_PAGE. |
97 | * @throws ApiUsageException |
98 | * @suppress PhanUndeclaredMethod |
99 | */ |
100 | private function decodeContinuationParameter( $continue, $mode, $sort ) { |
101 | if ( $continue === null ) { |
102 | return null; |
103 | } |
104 | |
105 | if ( $mode === self::$MODE_PAGE ) { |
106 | $this->dieContinueUsageIf( $continue !== (string)(int)$continue ); |
107 | return (int)$continue; |
108 | } else { |
109 | // Continue token format is '<name|timestamp>|<id>'; name can contain '|'. |
110 | $separatorPosition = strrpos( $continue, '|' ); |
111 | $this->dieContinueUsageIf( $separatorPosition === false ); |
112 | $continue = [ |
113 | substr( $continue, 0, $separatorPosition ), |
114 | substr( $continue, $separatorPosition + 1 ), |
115 | ]; |
116 | $this->dieContinueUsageIf( $continue[1] !== (string)(int)$continue[1] ); |
117 | $continue[1] = (int)$continue[1]; |
118 | if ( $sort === ReadingListRepository::SORT_BY_UPDATED ) { |
119 | $this->dieContinueUsageIf( wfTimestamp( TS_MW, $continue[0] ) === false ); |
120 | } |
121 | return $continue; |
122 | } |
123 | } |
124 | |
125 | /** |
126 | * Get common sorting/paging related params for getAllowedParams(). |
127 | * @return array |
128 | * @suppress PhanUndeclaredStaticProperty, PhanUndeclaredConstantOfClass |
129 | */ |
130 | private function getAllowedSortParams() { |
131 | return [ |
132 | 'sort' => [ |
133 | ParamValidator::PARAM_TYPE => [ 'name', 'updated' ], |
134 | self::PARAM_HELP_MSG_PER_VALUE => [], |
135 | ], |
136 | 'dir' => [ |
137 | ParamValidator::PARAM_DEFAULT => 'ascending', |
138 | ParamValidator::PARAM_TYPE => [ 'ascending', 'descending' ], |
139 | ], |
140 | 'limit' => [ |
141 | ParamValidator::PARAM_DEFAULT => 10, |
142 | ParamValidator::PARAM_TYPE => 'limit', |
143 | self::PARAM_MIN => 1, |
144 | // Temporarily limit paging sizes per T164990#3264314 / T168984#3659998 |
145 | self::PARAM_MAX => self::$prefix === 'rl' ? 10 : 100, |
146 | self::PARAM_MAX2 => self::$prefix === 'rl' ? 10 : 100, |
147 | ], |
148 | 'continue' => [ |
149 | ParamValidator::PARAM_TYPE => 'string', |
150 | ParamValidator::PARAM_DEFAULT => null, |
151 | self::PARAM_HELP_MSG => 'api-help-param-continue', |
152 | ], |
153 | ]; |
154 | } |
155 | |
156 | } |