|
16 | 16 |
|
17 | 17 | import asyncio
|
18 | 18 | import sys
|
19 |
| -from typing import Callable, Dict, List, Tuple, Optional, Union, Type |
| 19 | +from typing import ( |
| 20 | + Callable, |
| 21 | + Dict, |
| 22 | + List, |
| 23 | + Optional, |
| 24 | + Tuple, |
| 25 | + Type, |
| 26 | + Union, |
| 27 | +) |
20 | 28 |
|
21 | 29 | from ansimarkup import ansiprint
|
22 | 30 |
|
| 31 | +from cylc.flow import __version__ as CYLC_VERSION |
23 | 32 | from cylc.flow.async_util import unordered_map
|
24 |
| -from cylc.flow.exceptions import CylcError, WorkflowStopped |
| 33 | +from cylc.flow.exceptions import ( |
| 34 | + CylcError, |
| 35 | + WorkflowStopped, |
| 36 | +) |
25 | 37 | import cylc.flow.flags
|
26 | 38 | from cylc.flow.id_cli import parse_ids_async
|
27 | 39 | from cylc.flow.terminal import DIM
|
28 | 40 |
|
29 | 41 |
|
| 42 | +# Known error messages for incompatibilites between this version of Cylc (that |
| 43 | +# is running the command) and the version of Cylc running the workflow: |
| 44 | +KNOWN_INCOMPAT = { |
| 45 | + 'Unknown argument "onResume" on field "trigger" of type "Mutations".', |
| 46 | +} |
| 47 | + |
| 48 | + |
30 | 49 | def call_multi(*args, **kwargs):
|
31 | 50 | """Call a function for each workflow in a list of IDs.
|
32 | 51 |
|
@@ -220,21 +239,36 @@ def _process_response(
|
220 | 239 |
|
221 | 240 |
|
222 | 241 | def _report(
|
223 |
| - response: dict, |
| 242 | + response: Union[dict, list], |
224 | 243 | ) -> Tuple[Optional[str], Optional[str], bool]:
|
225 | 244 | """Report the result of a GraphQL operation.
|
226 | 245 |
|
227 | 246 | This analyses GraphQL mutation responses to determine the outcome.
|
228 | 247 |
|
229 | 248 | Args:
|
230 |
| - response: The GraphQL response. |
| 249 | + response: The workflow server response (NOT necessarily conforming to |
| 250 | + GraphQL execution result spec). |
231 | 251 |
|
232 | 252 | Returns:
|
233 | 253 | (stdout, stderr, outcome)
|
234 | 254 |
|
235 | 255 | """
|
236 | 256 | try:
|
237 | 257 | ret: List[Tuple[Optional[str], Optional[str], bool]] = []
|
| 258 | + if not isinstance(response, dict): |
| 259 | + if isinstance(response, list) and response[0].get('error'): |
| 260 | + # If operating on workflow running in older Cylc version, |
| 261 | + # may get a error response like [{'error': '...'}] |
| 262 | + if response[0]['error'].get('message') in KNOWN_INCOMPAT: |
| 263 | + raise Exception( |
| 264 | + "This command is no longer compatible with the " |
| 265 | + "version of Cylc running the workflow. Please stop & " |
| 266 | + f"restart the workflow with Cylc {CYLC_VERSION} " |
| 267 | + "or higher." |
| 268 | + f"\n\n{response}" |
| 269 | + ) |
| 270 | + raise Exception(response) |
| 271 | + raise Exception(f"Unexpected response: {response}") |
238 | 272 | for mutation_response in response.values():
|
239 | 273 | # extract the result of each mutation result in the response
|
240 | 274 | success, msg = mutation_response['result'][0]['response']
|
@@ -268,7 +302,7 @@ def _report(
|
268 | 302 | # response returned is not in the expected format - this shouldn't
|
269 | 303 | # happen but we need to protect against it
|
270 | 304 | err_msg = ''
|
271 |
| - if cylc.flow.flags.verbosity > 1: # debug mode |
| 305 | + if cylc.flow.flags.verbosity > 0: # verbose mode |
272 | 306 | # print the full result to stderr
|
273 | 307 | err_msg += f'\n <{DIM}>response={response}</{DIM}>'
|
274 | 308 | return (
|
|
0 commit comments