Passed
Push — master ( f1fe9e...5c5de8 )
by
unknown
03:44
created

ActionExecutionOutputAPI   A

Complexity

Total Complexity 3

Size/Duplication

Total Lines 38
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 38
rs 10
wmc 3

1 Method

Rating   Name   Duplication   Size   Complexity  
A from_model() 0 7 3
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
import six
19
20
from st2common.constants.action import LIVEACTION_STATUSES
21
from st2common.util import isotime
22
from st2common.models.api.base import BaseAPI
23
from st2common.models.db.execution import ActionExecutionDB
24
from st2common.models.db.execution import ActionExecutionOutputDB
25
from st2common.models.api.trigger import TriggerTypeAPI, TriggerAPI, TriggerInstanceAPI
26
from st2common.models.api.rule import RuleAPI
27
from st2common.models.api.action import RunnerTypeAPI, ActionAPI, LiveActionAPI
28
from st2common import log as logging
29
30
__all__ = [
31
    'ActionExecutionAPI',
32
    'ActionExecutionOutputAPI'
33
]
34
35
36
LOG = logging.getLogger(__name__)
37
38
REQUIRED_ATTR_SCHEMAS = {
39
    "action": copy.deepcopy(ActionAPI.schema),
40
    "runner": copy.deepcopy(RunnerTypeAPI.schema),
41
    "liveaction": copy.deepcopy(LiveActionAPI.schema),
42
}
43
44
for k, v in six.iteritems(REQUIRED_ATTR_SCHEMAS):
45
    v.update({"required": True})
46
47
48
class ActionExecutionAPI(BaseAPI):
49
    model = ActionExecutionDB
50
    SKIP = ['start_timestamp', 'end_timestamp']
51
    schema = {
52
        "title": "ActionExecution",
53
        "description": "Record of the execution of an action.",
54
        "type": "object",
55
        "properties": {
56
            "id": {
57
                "type": "string",
58
                "required": True
59
            },
60
            "trigger": TriggerAPI.schema,
61
            "trigger_type": TriggerTypeAPI.schema,
62
            "trigger_instance": TriggerInstanceAPI.schema,
63
            "rule": RuleAPI.schema,
64
            "action": REQUIRED_ATTR_SCHEMAS['action'],
65
            "runner": REQUIRED_ATTR_SCHEMAS['runner'],
66
            "liveaction": REQUIRED_ATTR_SCHEMAS['liveaction'],
67
            "status": {
68
                "description": "The current status of the action execution.",
69
                "type": "string",
70
                "enum": LIVEACTION_STATUSES
71
            },
72
            "start_timestamp": {
73
                "description": "The start time when the action is executed.",
74
                "type": "string",
75
                "pattern": isotime.ISO8601_UTC_REGEX
76
            },
77
            "end_timestamp": {
78
                "description": "The timestamp when the action has finished.",
79
                "type": "string",
80
                "pattern": isotime.ISO8601_UTC_REGEX
81
            },
82
            "elapsed_seconds": {
83
                "description": "Time duration in seconds taken for completion of this execution.",
84
                "type": "number",
85
                "required": False
86
            },
87
            "web_url": {
88
                "description": "History URL for this execution if you want to view in UI.",
89
                "type": "string",
90
                "required": False
91
            },
92
            "parameters": {
93
                "description": "Input parameters for the action.",
94
                "type": "object",
95
                "patternProperties": {
96
                    "^\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...
97
                        "anyOf": [
98
                            {"type": "array"},
99
                            {"type": "boolean"},
100
                            {"type": "integer"},
101
                            {"type": "number"},
102
                            {"type": "object"},
103
                            {"type": "string"}
104
                        ]
105
                    }
106
                },
107
                'additionalProperties': False
108
            },
109
            "context": {
110
                "type": "object"
111
            },
112
            "result": {
113
                "anyOf": [{"type": "array"},
114
                          {"type": "boolean"},
115
                          {"type": "integer"},
116
                          {"type": "number"},
117
                          {"type": "object"},
118
                          {"type": "string"}]
119
            },
120
            "parent": {"type": "string"},
121
            "children": {
122
                "type": "array",
123
                "items": {"type": "string"},
124
                "uniqueItems": True
125
            },
126
            "log": {
127
                "description": "Contains information about execution state transitions.",
128
                "type": "array",
129
                "items": {
130
                    "type": "object",
131
                    "properties": {
132
                        "timestamp": {
133
                            "type": "string",
134
                            "pattern": isotime.ISO8601_UTC_REGEX
135
                        },
136
                        "status": {
137
                            "type": "string",
138
                            "enum": LIVEACTION_STATUSES
139
                        }
140
                    }
141
                }
142
            }
143
        },
144
        "additionalProperties": False
145
    }
146
147
    @classmethod
148
    def from_model(cls, model, mask_secrets=False):
149
        doc = cls._from_model(model, mask_secrets=mask_secrets)
150
        start_timestamp = model.start_timestamp
151
        start_timestamp_iso = isotime.format(start_timestamp, offset=False)
152
        doc['start_timestamp'] = start_timestamp_iso
153
154
        end_timestamp = model.end_timestamp
155
        if end_timestamp:
156
            end_timestamp_iso = isotime.format(end_timestamp, offset=False)
157
            doc['end_timestamp'] = end_timestamp_iso
158
            doc['elapsed_seconds'] = (end_timestamp - start_timestamp).total_seconds()
159
160
        for entry in doc.get('log', []):
161
            entry['timestamp'] = isotime.format(entry['timestamp'], offset=False)
162
163
        attrs = {attr: value for attr, value in six.iteritems(doc) if value}
164
        return cls(**attrs)
165
166
    @classmethod
167
    def to_model(cls, instance):
168
        values = {}
169
        for attr, meta in six.iteritems(cls.schema.get('properties', dict())):
170
            if not getattr(instance, attr, None):
171
                continue
172
173
            default = copy.deepcopy(meta.get('default', None))
174
            value = getattr(instance, attr, default)
175
176
            # pylint: disable=no-member
177
            # TODO: Add plugin which lets pylint know each MongoEngine document has _fields
178
            # attribute
179
            attr_schema = cls.model._fields.get(attr, None)
180
            if not value and (attr_schema and not attr_schema.required):
181
                continue
182
            if attr not in ActionExecutionAPI.SKIP:
183
                values[attr] = value
184
185
        values['start_timestamp'] = isotime.parse(instance.start_timestamp)
186
        values['end_timestamp'] = isotime.parse(instance.end_timestamp)
187
188
        model = cls.model(**values)
189
        return model
190
191
192
class ActionExecutionOutputAPI(BaseAPI):
193
    model = ActionExecutionOutputDB
194
    schema = {
195
        'type': 'object',
196
        'properties': {
197
            'id': {
198
                'type': 'string'
199
            },
200
            'execution_id': {
201
                'type': 'string'
202
            },
203
            'action_ref': {
204
                'type': 'string'
205
            },
206
            'runner_ref': {
207
                'type': 'string'
208
            },
209
            'timestamp': {
210
                'type': 'string',
211
                'pattern': isotime.ISO8601_UTC_REGEX
212
            },
213
            'output_type': {
214
                'type': 'string'
215
            },
216
            'data': {
217
                'type': 'string'
218
            }
219
        },
220
        'additionalProperties': False
221
    }
222
223
    @classmethod
224
    def from_model(cls, model, mask_secrets=True):
225
        doc = cls._from_model(model, mask_secrets=mask_secrets)
226
        doc['timestamp'] = isotime.format(model.timestamp, offset=False)
227
228
        attrs = {attr: value for attr, value in six.iteritems(doc) if value is not None}
229
        return cls(**attrs)
230