Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 52 |
|
0.00% |
0 / 9 |
CRAP | |
0.00% |
0 / 1 |
QtiCommunicationService | |
0.00% |
0 / 52 |
|
0.00% |
0 / 9 |
420 | |
0.00% |
0 / 1 |
processInput | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
42 | |||
processOutput | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
20 | |||
attachChannel | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
6 | |||
detachChannel | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
6 | |||
hasChannel | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getChannel | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
islogInputEnabled | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
processChannel | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
fallback | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | /** |
4 | * This program is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU General Public License |
6 | * as published by the Free Software Foundation; under version 2 |
7 | * of the License (non-upgradable). |
8 | * |
9 | * This program is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | * GNU General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU General Public License |
15 | * along with this program; if not, write to the Free Software |
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
17 | * |
18 | * Copyright (c) 2016 (original work) Open Assessment Technologies SA ; |
19 | */ |
20 | |
21 | /** |
22 | * @author Jean-Sébastien Conan <jean-sebastien.conan@vesperiagroup.com> |
23 | */ |
24 | |
25 | namespace oat\taoQtiTest\models\runner\communicator; |
26 | |
27 | use common_exception_InconsistentData; |
28 | use oat\oatbox\service\ConfigurableService; |
29 | use oat\taoQtiTest\models\runner\QtiRunnerServiceContext; |
30 | |
31 | /** |
32 | * Class QtiCommunicationService |
33 | * |
34 | * Implements a bidirectional communication between client and server using polling |
35 | * |
36 | * @package oat\taoQtiTest\models\runner\communicator |
37 | */ |
38 | class QtiCommunicationService extends ConfigurableService implements CommunicationService |
39 | { |
40 | public const SERVICE_ID = 'taoQtiTest/QtiCommunicationService'; |
41 | |
42 | /** |
43 | * @deprecated use SERVICE_ID |
44 | */ |
45 | public const CONFIG_ID = 'taoQtiTest/QtiCommunicationService'; |
46 | |
47 | public const OPTION_CHANNELS = 'channels'; |
48 | public const OPTION_LOG_INPUT = 'log_input'; |
49 | |
50 | /** |
51 | * Processes the input messages |
52 | * @param QtiRunnerServiceContext $context - Needs the current runner context |
53 | * @param array $input - Accept a list of input, each one is represented by a channel's name that is a string and a |
54 | * message that can be any type |
55 | * @return array - Returns a list of responses in the same order as the input list |
56 | * @throws common_exception_InconsistentData |
57 | */ |
58 | public function processInput(QtiRunnerServiceContext $context, array $input): array |
59 | { |
60 | $responses = []; |
61 | |
62 | if ($this->islogInputEnabled()) { |
63 | $this->logInfo('Test runner message: ' . json_encode($input)); |
64 | } |
65 | |
66 | foreach ($input as $data) { |
67 | if (!is_array($data) || !isset($data['channel'], $data['message'])) { |
68 | throw new common_exception_InconsistentData( |
69 | 'Wrong message chunk received by the bidirectional communication service: either channel ' |
70 | . 'or message content is missing!' |
71 | ); |
72 | } |
73 | |
74 | if ($this->hasChannel($data['channel'], self::CHANNEL_TYPE_INPUT)) { |
75 | $channel = $this->getChannel($data['channel'], self::CHANNEL_TYPE_INPUT); |
76 | // known channel, forward... |
77 | $responses[] = $this->processChannel($channel, $context, $data['message']); |
78 | } else { |
79 | // unknown channel, fallback! |
80 | $responses[] = $this->fallback($data['channel'], $context, $data['message']); |
81 | } |
82 | } |
83 | |
84 | return $responses; |
85 | } |
86 | |
87 | /** |
88 | * Builds the list of output messages |
89 | * @param QtiRunnerServiceContext $context - Needs the current runner context |
90 | * @return array - Returns a list of output, each one is represented by a channel's name that is a string and a |
91 | * message that can be any type |
92 | */ |
93 | public function processOutput(QtiRunnerServiceContext $context): array |
94 | { |
95 | $messages = []; |
96 | $channels = $this->getOption(self::OPTION_CHANNELS); |
97 | if (is_array($channels[self::CHANNEL_TYPE_OUTPUT])) { |
98 | foreach ($channels[self::CHANNEL_TYPE_OUTPUT] as $outputChannelName => $outputChannelClass) { |
99 | $channel = $this->getChannel($outputChannelName, self::CHANNEL_TYPE_OUTPUT); |
100 | $message = $this->processChannel($channel, $context); |
101 | if ($message !== null) { |
102 | $messages[] = [ |
103 | 'channel' => $channel->getName(), |
104 | 'message' => $message, |
105 | ]; |
106 | } |
107 | } |
108 | } |
109 | return $messages; |
110 | } |
111 | |
112 | /** |
113 | * @param CommunicationChannel $channel |
114 | * @param integer $channelType |
115 | * @throws common_exception_InconsistentData |
116 | */ |
117 | public function attachChannel(CommunicationChannel $channel, $channelType): void |
118 | { |
119 | if ($this->hasChannel($channel->getName(), $channelType)) { |
120 | throw new common_exception_InconsistentData( |
121 | 'Channel ' . $channel->getName() . ' already registered in ' . __CLASS__ |
122 | ); |
123 | } |
124 | |
125 | $channels = $this->getOption(self::OPTION_CHANNELS); |
126 | |
127 | $channels[$channelType][$channel->getName()] = get_class($channel); |
128 | $this->setOption(self::OPTION_CHANNELS, $channels); |
129 | } |
130 | |
131 | /** |
132 | * @param CommunicationChannel $channel |
133 | * @param integer $channelType |
134 | * @throws common_exception_InconsistentData |
135 | */ |
136 | public function detachChannel(CommunicationChannel $channel, $channelType): void |
137 | { |
138 | if (!$this->hasChannel($channel->getName(), $channelType)) { |
139 | throw new common_exception_InconsistentData( |
140 | 'Channel ' . $channel->getName() . 'is not registered in ' . __CLASS__ |
141 | ); |
142 | } |
143 | |
144 | $channels = $this->getOption(self::OPTION_CHANNELS); |
145 | unset($channels[$channelType][$channel->getName()]); |
146 | $this->setOption(self::OPTION_CHANNELS, $channels); |
147 | } |
148 | |
149 | /** |
150 | * Check whether channel exists |
151 | * @param string $channelName |
152 | * @param integer $channelType |
153 | * @return bool |
154 | */ |
155 | protected function hasChannel($channelName, $channelType): bool |
156 | { |
157 | $channels = $this->getOption(self::OPTION_CHANNELS); |
158 | return isset($channels[$channelType][$channelName]); |
159 | } |
160 | |
161 | /** |
162 | * @param string $channelName |
163 | * @param integer $channelType |
164 | * @return CommunicationChannel |
165 | */ |
166 | protected function getChannel($channelName, $channelType): CommunicationChannel |
167 | { |
168 | $channels = $this->getOption(self::OPTION_CHANNELS); |
169 | $channel = new $channels[$channelType][$channelName](); |
170 | $this->propagate($channel); |
171 | |
172 | return $channel; |
173 | } |
174 | |
175 | /** |
176 | * @return bool |
177 | */ |
178 | private function islogInputEnabled(): bool |
179 | { |
180 | if (!$this->hasOption(self::OPTION_LOG_INPUT)) { |
181 | return false; |
182 | } |
183 | return filter_var($this->getOption(self::OPTION_LOG_INPUT), FILTER_VALIDATE_BOOLEAN); |
184 | } |
185 | |
186 | /** |
187 | * @param QtiRunnerServiceContext $context |
188 | * @param CommunicationChannel $channel |
189 | * @param array $data |
190 | * @return mixed channel response |
191 | */ |
192 | protected function processChannel(CommunicationChannel $channel, QtiRunnerServiceContext $context, array $data = []) |
193 | { |
194 | return $channel->process($context, $data); |
195 | } |
196 | |
197 | /** |
198 | * Fallback for unknown channels |
199 | * @param QtiRunnerServiceContext $context |
200 | * @param string $channel |
201 | * @param mixed $message |
202 | * @return mixed |
203 | */ |
204 | protected function fallback($channel, QtiRunnerServiceContext $context, $message) |
205 | { |
206 | // do nothing by default, need to be overwritten |
207 | return null; |
208 | } |
209 | } |