#!/usr/bin/env python3

<<<<<<< HEAD:cylc/flow/scripts/cylc_ext_trigger.py
# THIS FILE IS PART OF THE CYLC SUITE ENGINE.
=======
# THIS FILE IS PART OF THE CYLC WORKFLOW ENGINE.
>>>>>>> upstream/8.0_b2:cylc/flow/scripts/ext_trigger.py
# Copyright (C) NIWA & British Crown (Met Office) & Contributors.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""cylc ext-trigger [OPTIONS] ARGS

<<<<<<< HEAD:cylc/flow/scripts/cylc_ext_trigger.py
Report an external event message to a suite server program. It is expected that
a task in the suite has registered the same message as an external trigger - a
special prerequisite to be satisfied by an external system, via this command,
rather than by triggering off other tasks.
=======
Report an external event message to a scheduler.

It is expected that a task in the workflow has registered the same message as
an external trigger - a special prerequisite to be satisfied by an external
system, via this command, rather than by triggering off other tasks.
>>>>>>> upstream/8.0_b2:cylc/flow/scripts/ext_trigger.py

The ID argument should uniquely distinguish one external trigger event from the
next. When a task's external trigger is satisfied by an incoming message, the
message ID is broadcast to all downstream tasks in the cycle point as
$CYLC_EXT_TRIGGER_ID so that they can use it - e.g. to identify a new data file
that the external triggering system is responding to.

<<<<<<< HEAD:cylc/flow/scripts/cylc_ext_trigger.py
Use the retry options in case the target suite is down or out of contact.

Note: to manually trigger a task use 'cylc trigger', not this command."""

=======
Use the retry options in case the target workflow is down or out of contact.

Note: to manually trigger a task use 'cylc trigger', not this command."""

import os.path
>>>>>>> upstream/8.0_b2:cylc/flow/scripts/ext_trigger.py
from time import sleep

from cylc.flow import LOG
from cylc.flow.exceptions import CylcError, ClientError
<<<<<<< HEAD:cylc/flow/scripts/cylc_ext_trigger.py
from cylc.flow.option_parsers import CylcOptionParser as COP
from cylc.flow.network.client import SuiteRuntimeClient
=======
from cylc.flow.network.client_factory import get_client
from cylc.flow.option_parsers import CylcOptionParser as COP
>>>>>>> upstream/8.0_b2:cylc/flow/scripts/ext_trigger.py
from cylc.flow.terminal import cli_function


MAX_N_TRIES = 5
RETRY_INTVL_SECS = 10.0

MSG_SEND_FAILED = "Send message: try %s of %s failed"
MSG_SEND_RETRY = "Retrying in %s seconds, timeout is %s"
MSG_SEND_SUCCEED = "Send message: try %s of %s succeeded"

<<<<<<< HEAD:cylc/flow/scripts/cylc_ext_trigger.py
=======
MUTATION = '''
mutation (
  $wFlows: [WorkflowID]!,
  $eventMsg: String!,
  $eventId: String!
) {
  extTrigger (
    workflows: $wFlows,
    message: $eventMsg,
    id: $eventId
  ) {
    result
  }
}
'''

>>>>>>> upstream/8.0_b2:cylc/flow/scripts/ext_trigger.py

def get_option_parser():
    parser = COP(
        __doc__, comms=True,
        argdoc=[("REG", "Workflow name"),
                ("MSG", "External trigger message"),
                ("ID", "Unique trigger ID")])

    parser.add_option(
        "--max-tries", help="Maximum number of send attempts "
        "(default %s)." % MAX_N_TRIES, metavar="INT",
        action="store", default=MAX_N_TRIES, dest="max_n_tries")

    parser.add_option(
        "--retry-interval", help="Delay in seconds before retrying "
        "(default %s)." % RETRY_INTVL_SECS, metavar="SEC",
        action="store", default=RETRY_INTVL_SECS, dest="retry_intvl_secs")

    return parser
<<<<<<< HEAD:cylc/flow/scripts/cylc_ext_trigger.py


@cli_function(get_option_parser)
def main(parser, options, suite, event_msg, event_id):
    LOG.info('Send to suite %s: "%s" (%s)', suite, event_msg, event_id)

    pclient = SuiteRuntimeClient(
        suite, options.owner, options.host, options.port)
=======


@cli_function(get_option_parser)
def main(parser, options, workflow, event_msg, event_id):
    workflow = os.path.normpath(workflow)
    LOG.info('Send to workflow %s: "%s" (%s)', workflow, event_msg, event_id)
    pclient = get_client(workflow, timeout=options.comms_timeout)
>>>>>>> upstream/8.0_b2:cylc/flow/scripts/ext_trigger.py

    max_n_tries = int(options.max_n_tries)
    retry_intvl_secs = float(options.retry_intvl_secs)

    mutation_kwargs = {
        'request_string': MUTATION,
        'variables': {
            'wFlows': [workflow],
            'eventMsg': event_msg,
            'eventId': event_id,
        }
    }

    for i_try in range(max_n_tries):
        try:
<<<<<<< HEAD:cylc/flow/scripts/cylc_ext_trigger.py
            pclient(
                'put_ext_trigger',
                {'message': event_msg, 'id': event_id},
                timeout=options.comms_timeout
            )
=======
            pclient('graphql', mutation_kwargs)
>>>>>>> upstream/8.0_b2:cylc/flow/scripts/ext_trigger.py
        except ClientError as exc:
            LOG.exception(exc)
            LOG.info(MSG_SEND_FAILED, i_try + 1, max_n_tries)
            if i_try == max_n_tries - 1:  # final attempt
                raise CylcError('send failed')
<<<<<<< HEAD:cylc/flow/scripts/cylc_ext_trigger.py
            else:
                LOG.info(MSG_SEND_RETRY, retry_intvl_secs,
                         options.comms_timeout)
                sleep(retry_intvl_secs)
=======
            LOG.info(MSG_SEND_RETRY, retry_intvl_secs, options.comms_timeout)
            sleep(retry_intvl_secs)
>>>>>>> upstream/8.0_b2:cylc/flow/scripts/ext_trigger.py
        else:
            if i_try > 0:
                LOG.info(MSG_SEND_SUCCEED, i_try + 1, max_n_tries)
            break


if __name__ == "__main__":
    main()
