Passed
Push — master ( 34f323...e86776 )
by Plexxi
02:43
created

ActionAliasHelpAPI   A

Complexity

Total Complexity 2

Size/Duplication

Total Lines 46
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 46
rs 10
c 1
b 0
f 0
wmc 2

2 Methods

Rating   Name   Duplication   Size   Complexity  
A from_model() 0 3 1
A to_model() 0 3 1
1
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
2
# contributor license agreements.  See the NOTICE file distributed with
3
# this work for additional information regarding copyright ownership.
4
# The ASF licenses this file to You under the Apache License, Version 2.0
5
# (the "License"); you may not use this file except in compliance with
6
# the License.  You may obtain a copy of the License at
7
#
8
#     http://www.apache.org/licenses/LICENSE-2.0
9
#
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS,
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
15
16
import copy
17
18
from st2common.util import isotime
19
from st2common.util import schema as util_schema
20
from st2common import log as logging
21
from st2common.constants.pack import DEFAULT_PACK_NAME
22
from st2common.models.api.base import BaseAPI
23
from st2common.models.api.base import APIUIDMixin
24
from st2common.models.api.tag import TagsHelper
25
from st2common.models.api.notification import (NotificationSubSchemaAPI, NotificationsHelper)
26
from st2common.models.db.action import ActionDB
27
from st2common.models.db.actionalias import ActionAliasDB
28
from st2common.models.db.executionstate import ActionExecutionStateDB
29
from st2common.models.db.liveaction import LiveActionDB
30
from st2common.models.db.runner import RunnerTypeDB
31
from st2common.constants.action import LIVEACTION_STATUSES
32
from st2common.models.system.common import ResourceReference
33
34
35
__all__ = [
36
    'ActionAPI',
37
    'ActionCreateAPI',
38
    'LiveActionAPI',
39
    'LiveActionCreateAPI',
40
    'RunnerTypeAPI',
41
42
    'AliasExecutionAPI',
43
    'ActionAliasAPI',
44
    'ActionAliasMatchAPI',
45
    'ActionAliasHelpAPI'
46
]
47
48
49
LOG = logging.getLogger(__name__)
50
51
52
class RunnerTypeAPI(BaseAPI):
53
    """
54
    The representation of an RunnerType in the system. An RunnerType
55
    has a one-to-one mapping to a particular ActionRunner implementation.
56
    """
57
    model = RunnerTypeDB
58
    schema = {
59
        "title": "Runner",
60
        "description": "A handler for a specific type of actions.",
61
        "type": "object",
62
        "properties": {
63
            "id": {
64
                "description": "The unique identifier for the action runner.",
65
                "type": "string",
66
                "default": None
67
            },
68
            "uid": {
69
                "type": "string"
70
            },
71
            "name": {
72
                "description": "The name of the action runner.",
73
                "type": "string",
74
                "required": True
75
            },
76
            "description": {
77
                "description": "The description of the action runner.",
78
                "type": "string"
79
            },
80
            "enabled": {
81
                "description": "Enable or disable the action runner.",
82
                "type": "boolean",
83
                "default": True
84
            },
85
            "runner_module": {
86
                "description": "The python module that implements the "
87
                               "action runner for this type.",
88
                "type": "string",
89
                "required": True
90
            },
91
            "query_module": {
92
                "description": "The python module that implements the "
93
                               "results tracker (querier) for the runner.",
94
                "type": "string",
95
                "required": False
96
            },
97
            "runner_parameters": {
98
                "description": "Input parameters for the action runner.",
99
                "type": "object",
100
                "patternProperties": {
101
                    "^\w+$": util_schema.get_action_parameters_schema()
0 ignored issues
show
Bug introduced by
A suspicious escape sequence \w was found. Did you maybe forget to add an r prefix?

Escape sequences in Python are generally interpreted according to rules similar to standard C. Only if strings are prefixed with r or R are they interpreted as regular expressions.

The escape sequence that was used indicates that you might have intended to write a regular expression.

Learn more about the available escape sequences. in the Python documentation.

Loading history...
102
                },
103
                'additionalProperties': False
104
            }
105
        },
106
        "additionalProperties": False
107
    }
108
109
    def __init__(self, **kw):
0 ignored issues
show
Bug introduced by
The __init__ method of the super-class BaseAPI is not called.

It is generally advisable to initialize the super-class by calling its __init__ method:

class SomeParent:
    def __init__(self):
        self.x = 1

class SomeChild(SomeParent):
    def __init__(self):
        # Initialize the super class
        SomeParent.__init__(self)
Loading history...
110
        # Ideally, you should not do that. You should not redefine __init__ to validate and then set
111
        # default values, instead you should define defaults in schema and use BaseAPI __init__
112
        # validator to unwrap them. The problem here is that draft schema also contains default
113
        # values and we don't want them to be unwrapped at the same time. I've tried to remove the
114
        # default values from draft schema, but, either because of a bug or some weird intention, it
115
        # has continued to resolve $ref'erenced properties against the initial draft schema, not the
116
        # modified one
117
        for key, value in kw.items():
118
            setattr(self, key, value)
119
        if not hasattr(self, 'runner_parameters'):
120
            setattr(self, 'runner_parameters', dict())
121
122
    @classmethod
123
    def to_model(cls, runner_type):
124
        name = runner_type.name
125
        description = runner_type.description
126
        enabled = getattr(runner_type, 'enabled', True)
127
        runner_module = str(runner_type.runner_module)
128
        runner_parameters = getattr(runner_type, 'runner_parameters', dict())
129
        query_module = getattr(runner_type, 'query_module', None)
130
131
        model = cls.model(name=name, description=description, enabled=enabled,
132
                          runner_module=runner_module, runner_parameters=runner_parameters,
133
                          query_module=query_module)
134
135
        return model
136
137
138
class ActionAPI(BaseAPI, APIUIDMixin):
139
    """
140
    The system entity that represents a Stack Action/Automation in the system.
141
    """
142
143
    model = ActionDB
144
    schema = {
145
        "title": "Action",
146
        "description": "An activity that happens as a response to the external event.",
147
        "type": "object",
148
        "properties": {
149
            "id": {
150
                "description": "The unique identifier for the action.",
151
                "type": "string"
152
            },
153
            "ref": {
154
                "description": "System computed user friendly reference for the action. \
155
                                Provided value will be overridden by computed value.",
156
                "type": "string"
157
            },
158
            "uid": {
159
                "type": "string"
160
            },
161
            "name": {
162
                "description": "The name of the action.",
163
                "type": "string",
164
                "required": True
165
            },
166
            "description": {
167
                "description": "The description of the action.",
168
                "type": "string"
169
            },
170
            "enabled": {
171
                "description": "Enable or disable the action from invocation.",
172
                "type": "boolean",
173
                "default": True
174
            },
175
            "runner_type": {
176
                "description": "The type of runner that executes the action.",
177
                "type": "string",
178
                "required": True
179
            },
180
            "entry_point": {
181
                "description": "The entry point for the action.",
182
                "type": "string",
183
                "default": ""
184
            },
185
            "pack": {
186
                "description": "The content pack this action belongs to.",
187
                "type": "string",
188
                "default": DEFAULT_PACK_NAME
189
            },
190
            "parameters": {
191
                "description": "Input parameters for the action.",
192
                "type": "object",
193
                "patternProperties": {
194
                    "^\w+$": util_schema.get_action_parameters_schema()
0 ignored issues
show
Bug introduced by
A suspicious escape sequence \w was found. Did you maybe forget to add an r prefix?

Escape sequences in Python are generally interpreted according to rules similar to standard C. Only if strings are prefixed with r or R are they interpreted as regular expressions.

The escape sequence that was used indicates that you might have intended to write a regular expression.

Learn more about the available escape sequences. in the Python documentation.

Loading history...
195
                },
196
                'additionalProperties': False,
197
                "default": {}
198
            },
199
            "tags": {
200
                "description": "User associated metadata assigned to this object.",
201
                "type": "array",
202
                "items": {"type": "object"}
203
            },
204
            "notify": {
205
                "description": "Notification settings for action.",
206
                "type": "object",
207
                "properties": {
208
                    "on-complete": NotificationSubSchemaAPI,
209
                    "on-failure": NotificationSubSchemaAPI,
210
                    "on-success": NotificationSubSchemaAPI
211
                },
212
                "additionalProperties": False
213
            }
214
        },
215
        "additionalProperties": False
216
    }
217
218
    def __init__(self, **kw):
0 ignored issues
show
Bug introduced by
The __init__ method of the super-class BaseAPI is not called.

It is generally advisable to initialize the super-class by calling its __init__ method:

class SomeParent:
    def __init__(self):
        self.x = 1

class SomeChild(SomeParent):
    def __init__(self):
        # Initialize the super class
        SomeParent.__init__(self)
Loading history...
219
        for key, value in kw.items():
220
            setattr(self, key, value)
221
        if not hasattr(self, 'parameters'):
222
            setattr(self, 'parameters', dict())
223
        if not hasattr(self, 'entry_point'):
224
            setattr(self, 'entry_point', '')
225
226
    @classmethod
227
    def from_model(cls, model, mask_secrets=False):
228
        action = cls._from_model(model)
229
        action['runner_type'] = action['runner_type']['name']
230
        action['tags'] = TagsHelper.from_model(model.tags)
231
232
        if getattr(model, 'notify', None):
233
            action['notify'] = NotificationsHelper.from_model(model.notify)
234
235
        return cls(**action)
236
237
    @classmethod
238
    def to_model(cls, action):
239
        name = getattr(action, 'name', None)
240
        description = getattr(action, 'description', None)
241
        enabled = bool(getattr(action, 'enabled', True))
242
        entry_point = str(action.entry_point)
243
        pack = str(action.pack)
244
        runner_type = {'name': str(action.runner_type)}
245
        parameters = getattr(action, 'parameters', dict())
246
        tags = TagsHelper.to_model(getattr(action, 'tags', []))
247
        ref = ResourceReference.to_string_reference(pack=pack, name=name)
248
249
        if getattr(action, 'notify', None):
250
            notify = NotificationsHelper.to_model(action.notify)
251
        else:
252
            # We use embedded document model for ``notify`` in action model. If notify is
253
            # set notify to None, Mongoengine interprets ``None`` as unmodified
254
            # field therefore doesn't delete the embedded document. Therefore, we need
255
            # to use an empty document.
256
            notify = NotificationsHelper.to_model({})
257
258
        model = cls.model(name=name, description=description, enabled=enabled,
259
                          entry_point=entry_point, pack=pack, runner_type=runner_type,
260
                          tags=tags, parameters=parameters, notify=notify,
261
                          ref=ref)
262
263
        return model
264
265
266
class ActionCreateAPI(ActionAPI, APIUIDMixin):
267
    """
268
    API model for create action operation.
269
    """
270
    schema = copy.deepcopy(ActionAPI.schema)
271
    schema['properties']['data_files'] = {
272
        'description': 'Optional action script and data files which are written to the filesystem.',
273
        'type': 'array',
274
        'items': {
275
            'type': 'object',
276
            'properties': {
277
                'file_path': {
278
                    'type': 'string',
279
                    'required': True
280
                },
281
                'content': {
282
                    'type': 'string',
283
                    'required': True
284
                },
285
            },
286
            'additionalProperties': False
287
        },
288
        'default': []
289
    }
290
291
292
class ActionUpdateAPI(ActionAPI, APIUIDMixin):
293
    """
294
    API model for update action operation.
295
    """
296
    schema = copy.deepcopy(ActionCreateAPI.schema)
297
    del schema['properties']['pack']['default']
298
299
300
class LiveActionAPI(BaseAPI):
301
    """The system entity that represents the execution of a Stack Action/Automation
302
    in the system.
303
    """
304
305
    model = LiveActionDB
306
    schema = {
307
        "title": "liveaction",
308
        "description": "An execution of an action.",
309
        "type": "object",
310
        "properties": {
311
            "id": {
312
                "description": "The unique identifier for the action execution.",
313
                "type": "string"
314
            },
315
            "status": {
316
                "description": "The current status of the action execution.",
317
                "type": "string",
318
                "enum": LIVEACTION_STATUSES
319
            },
320
            "start_timestamp": {
321
                "description": "The start time when the action is executed.",
322
                "type": "string",
323
                "pattern": isotime.ISO8601_UTC_REGEX
324
            },
325
            "end_timestamp": {
326
                "description": "The timestamp when the action has finished.",
327
                "type": "string",
328
                "pattern": isotime.ISO8601_UTC_REGEX
329
            },
330
            "action": {
331
                "description": "Reference to the action to be executed.",
332
                "type": "string",
333
                "required": True
334
            },
335
            "parameters": {
336
                "description": "Input parameters for the action.",
337
                "type": "object",
338
                "patternProperties": {
339
                    "^\w+$": {
0 ignored issues
show
Bug introduced by
A suspicious escape sequence \w was found. Did you maybe forget to add an r prefix?

Escape sequences in Python are generally interpreted according to rules similar to standard C. Only if strings are prefixed with r or R are they interpreted as regular expressions.

The escape sequence that was used indicates that you might have intended to write a regular expression.

Learn more about the available escape sequences. in the Python documentation.

Loading history...
340
                        "anyOf": [
341
                            {"type": "array"},
342
                            {"type": "boolean"},
343
                            {"type": "integer"},
344
                            {"type": "number"},
345
                            {"type": "object"},
346
                            {"type": "string"},
347
                            {"type": "null"}
348
                        ]
349
                    }
350
                },
351
                'additionalProperties': False
352
            },
353
            "result": {
354
                "anyOf": [{"type": "array"},
355
                          {"type": "boolean"},
356
                          {"type": "integer"},
357
                          {"type": "number"},
358
                          {"type": "object"},
359
                          {"type": "string"}]
360
            },
361
            "context": {
362
                "type": "object"
363
            },
364
            "callback": {
365
                "type": "object"
366
            },
367
            "runner_info": {
368
                "type": "object"
369
            },
370
            "notify": {
371
                "description": "Notification settings for liveaction.",
372
                "type": "object",
373
                "properties": {
374
                    "on-complete": NotificationSubSchemaAPI,
375
                    "on-failure": NotificationSubSchemaAPI,
376
                    "on-success": NotificationSubSchemaAPI
377
                },
378
                "additionalProperties": False
379
            }
380
        },
381
        "additionalProperties": False
382
    }
383
384
    @classmethod
385
    def from_model(cls, model, mask_secrets=False):
386
        doc = super(cls, cls)._from_model(model, mask_secrets=mask_secrets)
387
        if model.start_timestamp:
388
            doc['start_timestamp'] = isotime.format(model.start_timestamp, offset=False)
389
        if model.end_timestamp:
390
            doc['end_timestamp'] = isotime.format(model.end_timestamp, offset=False)
391
392
        if getattr(model, 'notify', None):
393
            doc['notify'] = NotificationsHelper.from_model(model.notify)
394
395
        return cls(**doc)
396
397
    @classmethod
398
    def to_model(cls, live_action):
399
        action = live_action.action
400
401
        if getattr(live_action, 'start_timestamp', None):
402
            start_timestamp = isotime.parse(live_action.start_timestamp)
403
        else:
404
            start_timestamp = None
405
406
        if getattr(live_action, 'end_timestamp', None):
407
            end_timestamp = isotime.parse(live_action.end_timestamp)
408
        else:
409
            end_timestamp = None
410
411
        status = getattr(live_action, 'status', None)
412
        parameters = getattr(live_action, 'parameters', dict())
413
        context = getattr(live_action, 'context', dict())
414
        callback = getattr(live_action, 'callback', dict())
415
        result = getattr(live_action, 'result', None)
416
417
        if getattr(live_action, 'notify', None):
418
            notify = NotificationsHelper.to_model(live_action.notify)
419
        else:
420
            notify = None
421
422
        model = cls.model(action=action,
423
                          start_timestamp=start_timestamp, end_timestamp=end_timestamp,
424
                          status=status, parameters=parameters, context=context,
425
                          callback=callback, result=result, notify=notify)
426
427
        return model
428
429
430
class LiveActionCreateAPI(LiveActionAPI):
431
    """
432
    API model for action execution create (run action) operations.
433
    """
434
    schema = copy.deepcopy(LiveActionAPI.schema)
435
    schema['properties']['user'] = {
436
        'description': 'User context under which action should run (admins only)',
437
        'type': 'string',
438
        'default': None
439
    }
440
441
442
class ActionExecutionStateAPI(BaseAPI):
443
    """
444
    System entity that represents state of an action in the system.
445
    This is used only in tests for now.
446
    """
447
    model = ActionExecutionStateDB
448
    schema = {
449
        "title": "ActionExecutionState",
450
        "description": "Execution state of an action.",
451
        "type": "object",
452
        "properties": {
453
            "id": {
454
                "description": "The unique identifier for the action execution state.",
455
                "type": "string"
456
            },
457
            "execution_id": {
458
                "type": "string",
459
                "description": "ID of the action execution.",
460
                "required": True
461
            },
462
            "query_context": {
463
                "type": "object",
464
                "description": "query context to be used by querier.",
465
                "required": True
466
            },
467
            "query_module": {
468
                "type": "string",
469
                "description": "Name of the query module.",
470
                "required": True
471
            }
472
        },
473
        "additionalProperties": False
474
    }
475
476
    @classmethod
477
    def to_model(cls, state):
478
        execution_id = state.execution_id
479
        query_module = state.query_module
480
        query_context = state.query_context
481
482
        model = cls.model(execution_id=execution_id, query_module=query_module,
483
                          query_context=query_context)
484
        return model
485
486
487
class ActionAliasAPI(BaseAPI, APIUIDMixin):
488
    """
489
    Alias for an action in the system.
490
    """
491
    model = ActionAliasDB
492
    schema = {
493
        "title": "ActionAlias",
494
        "description": "Alias for an action.",
495
        "type": "object",
496
        "properties": {
497
            "id": {
498
                "description": "The unique identifier for the action alias.",
499
                "type": "string"
500
            },
501
            "ref": {
502
                "description": "System computed user friendly reference for the alias. \
503
                                Provided value will be overridden by computed value.",
504
                "type": "string"
505
            },
506
            "uid": {
507
                "type": "string"
508
            },
509
            "name": {
510
                "type": "string",
511
                "description": "Name of the action alias.",
512
                "required": True
513
            },
514
            "pack": {
515
                "description": "The content pack this actionalias belongs to.",
516
                "type": "string",
517
                "required": True
518
            },
519
            "description": {
520
                "type": "string",
521
                "description": "Description of the action alias.",
522
                "default": None
523
            },
524
            "enabled": {
525
                "description": "Flag indicating of action alias is enabled.",
526
                "type": "boolean",
527
                "default": True
528
            },
529
            "action_ref": {
530
                "type": "string",
531
                "description": "Reference to the aliased action.",
532
                "required": True
533
            },
534
            "formats": {
535
                "type": "array",
536
                "items": {
537
                    "anyOf": [
538
                        {"type": "string"},
539
                        {
540
                            "type": "object",
541
                            "properties": {
542
                                "display": {"type": "string"},
543
                                "representation": {
544
                                    "type": "array",
545
                                    "items": {"type": "string"}
546
                                }
547
                            }
548
                        }
549
                    ]
550
                },
551
                "description": "Possible parameter format."
552
            },
553
            "ack": {
554
                "type": "object",
555
                "properties": {
556
                    "enabled": {"type": "boolean"},
557
                    "format": {"type": "string"},
558
                    "extra": {"type": "object"},
559
                    "append_url": {"type": "boolean"}
560
                },
561
                "description": "Acknowledgement message format."
562
            },
563
            "result": {
564
                "type": "object",
565
                "properties": {
566
                    "enabled": {"type": "boolean"},
567
                    "format": {"type": "string"},
568
                    "extra": {"type": "object"}
569
                },
570
                "description": "Execution message format."
571
            },
572
            "extra": {
573
                "type": "object",
574
                "description": "Extra parameters, usually adapter-specific."
575
            }
576
        },
577
        "additionalProperties": False
578
    }
579
580
    @classmethod
581
    def to_model(cls, alias):
582
        name = alias.name
583
        description = getattr(alias, 'description', None)
584
        pack = alias.pack
585
        ref = ResourceReference.to_string_reference(pack=pack, name=name)
586
        enabled = getattr(alias, 'enabled', True)
587
        action_ref = alias.action_ref
588
        formats = alias.formats
589
        ack = getattr(alias, 'ack', None)
590
        result = getattr(alias, 'result', None)
591
        extra = getattr(alias, 'extra', None)
592
593
        model = cls.model(name=name, description=description, pack=pack, ref=ref,
594
                          enabled=enabled, action_ref=action_ref, formats=formats,
595
                          ack=ack, result=result, extra=extra)
596
        return model
597
598
599
class AliasExecutionAPI(BaseAPI):
600
    """
601
    Alias for an action in the system.
602
    """
603
    model = None
604
    schema = {
605
        "title": "AliasExecution",
606
        "description": "Execution of an ActionAlias.",
607
        "type": "object",
608
        "properties": {
609
            "name": {
610
                "type": "string",
611
                "description": "Name of the action alias which matched.",
612
                "required": True
613
            },
614
            "format": {
615
                "type": "string",
616
                "description": "Format string which matched.",
617
                "required": True
618
            },
619
            "command": {
620
                "type": "string",
621
                "description": "Command used in chat.",
622
                "required": True
623
            },
624
            "user": {
625
                "type": "string",
626
                "description": "User that requested the execution.",
627
                "default": "channel"  # TODO: This value doesnt get set
628
            },
629
            "source_channel": {
630
                "type": "string",
631
                "description": "Channel from which the execution was requested. This is not the \
632
                                channel as defined by the notification system.",
633
                "required": True
634
            },
635
            "notification_channel": {
636
                "type": "string",
637
                "description": "StackStorm notification channel to use to respond.",
638
                "required": False
639
            },
640
            "notification_route": {
641
                "type": "string",
642
                "description": "StackStorm notification route to use to respond.",
643
                "required": False
644
            }
645
        },
646
        "additionalProperties": False
647
    }
648
649
    @classmethod
650
    def to_model(cls, aliasexecution):
651
        # probably should be unsupported
652
        raise NotImplementedError()
653
654
    @classmethod
655
    def from_model(cls, aliasexecution):
0 ignored issues
show
Bug introduced by
Arguments number differs from overridden 'from_model' method
Loading history...
656
        raise NotImplementedError()
657
658
659
class ActionAliasMatchAPI(BaseAPI):
660
    """
661
    API model used for alias match API endpoint.
662
    """
663
    model = None
664
665
    schema = {
666
        "title": "ActionAliasMatchAPI",
667
        "description": "ActionAliasMatchAPI.",
668
        "type": "object",
669
        "properties": {
670
            "command": {
671
                "type": "string",
672
                "description": "Command string to try to match the aliases against.",
673
                "required": True
674
            }
675
        },
676
        "additionalProperties": False
677
    }
678
679
    @classmethod
680
    def to_model(cls, aliasexecution):
681
        raise NotImplementedError()
682
683
    @classmethod
684
    def from_model(cls, aliasexecution):
0 ignored issues
show
Bug introduced by
Arguments number differs from overridden 'from_model' method
Loading history...
685
        raise NotImplementedError()
686
687
688
class ActionAliasHelpAPI(BaseAPI):
689
    """
690
    API model used to display action-alias help API endpoint.
691
    """
692
    model = None
693
694
    schema = {
695
        "title": "ActionAliasHelpAPI",
696
        "description": "ActionAliasHelpAPI.",
697
        "type": "object",
698
        "properties": {
699
            "filter": {
700
                "type": "string",
701
                "description": "Find help strings containing keyword.",
702
                "required": False,
703
                "default": ""
704
            },
705
            "pack": {
706
                "type": "string",
707
                "description": "List help strings for a specific pack.",
708
                "required": False,
709
                "default": ""
710
            },
711
            "offset": {
712
                "type": "integer",
713
                "description": "List help strings from the offset position.",
714
                "required": False,
715
                "default": 0
716
            },
717
            "limit": {
718
                "type": "integer",
719
                "description": "Limit the number of help strings returned.",
720
                "required": False,
721
                "default": 0
722
            }
723
        },
724
        "additionalProperties": False
725
    }
726
727
    @classmethod
728
    def to_model(cls, aliasexecution):
729
        raise NotImplementedError()
730
731
    @classmethod
732
    def from_model(cls, aliasexecution):
0 ignored issues
show
Bug introduced by
Arguments number differs from overridden 'from_model' method
Loading history...
733
        raise NotImplementedError()
734