Test Failed
Push — master ( ef441d...0acd10 )
by Heiko 'riot'
04:41 queued 10s
created

isomer.schemata.base.base_object()   F

Complexity

Conditions 16

Size

Total Lines 106
Code Lines 77

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 16
eloc 77
nop 12
dl 0
loc 106
rs 2.4
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like isomer.schemata.base.base_object() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
#!/usr/bin/env python
2
# -*- coding: UTF-8 -*-
3
4
# Isomer - The distributed application framework
5
# ==============================================
6
# Copyright (C) 2011-2020 Heiko 'riot' Weinen <[email protected]> and others.
7
#
8
# This program is free software: you can redistribute it and/or modify
9
# it under the terms of the GNU Affero General Public License as published by
10
# the Free Software Foundation, either version 3 of the License, or
11
# (at your option) any later version.
12
#
13
# This program is distributed in the hope that it will be useful,
14
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
# GNU Affero General Public License for more details.
17
#
18
# You should have received a copy of the GNU Affero General Public License
19
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
21
"""
22
23
Schema: Base
24
============
25
26
Basic Isomer object schema utilities
27
28
Contains
29
--------
30
31
uuid_object: For inserting UUID fields
32
base_object: For generating a basic Isomer object schema
33
34
35
"""
36
37
from isomer.misc import all_languages
38
39
40
def geo_coordinate(
41
        title="Coordinate", description="A coordinate", default=None, display=True
42
):
43
    """Generates geo coordinate field"""
44
45
    result = {
46
        "type": "object",
47
        "title": title,
48
        "description": description,
49
        "additionalProperties": False,
50
        "properties": {
51
            'lat': {
52
                'type': 'string',
53
                'pattern': "^[-+]?([1-8]?\d(\.\d+)?|90(\.0+)?)$",
54
                'title': 'Latitude',
55
                'description': 'From 90 Degrees North (+) to South (-)'
56
            },
57
            'lon': {
58
                'type': 'string',
59
                'pattern': '^[-+]?(180(\.0+)?|((1[0-7]\d)|([1-9]?\d))(\.\d+)?)$',
60
                'title': 'Longitude',
61
                'description': 'From 180 Degrees East (+) to West (-)'
62
            }
63
        }
64
    }
65
66
    if not display:
67
        result["x-schema-form"] = {"condition": "false"}
68
69
    if default is not None:
70
        result["default"] = default
71
72
    return result
73
74
75
def uuid_object(
76
        title="Reference", description="Select an object", default=None, display=True
77
):
78
    """Generates a regular expression controlled UUID field"""
79
80
    uuid = {
81
        "pattern": "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]"
82
                   "{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$",
83
        "type": "string",
84
        "title": title,
85
        "description": description,
86
    }
87
88
    if not display:
89
        uuid["x-schema-form"] = {"condition": "false"}
90
91
    if default is not None:
92
        uuid["default"] = default
93
94
    return uuid
95
96
97
def base_object(
98
        name,
99
        no_additional=False,
100
        no_perms=False,
101
        no_color=False,
102
        has_owner=True,
103
        hide_owner=True,
104
        has_uuid=True,
105
        roles_write=None,
106
        roles_read=None,
107
        roles_list=None,
108
        roles_create=None,
109
        all_roles=None,
110
):
111
    """Generates a basic object with RBAC properties"""
112
    base_schema = {"id": "#" + name, "type": "object", "name": name, "properties": {}}
113
114
    if no_additional:
115
        base_schema['additionalProperties'] = False
116
117
    if not no_perms:
118
        if all_roles:
119
            roles_create = ["admin", all_roles]
120
            roles_write = ["admin", all_roles]
121
            roles_read = ["admin", all_roles]
122
            roles_list = ["admin", all_roles]
123
        else:
124
            if roles_write is None:
125
                roles_write = ["admin"]
126
            if roles_read is None:
127
                roles_read = ["admin"]
128
            if roles_list is None:
129
                roles_list = ["admin"]
130
            if roles_create is None:
131
                roles_create = ["admin"]
132
133
        if isinstance(roles_create, str):
134
            roles_create = [roles_create]
135
        if isinstance(roles_write, str):
136
            roles_write = [roles_write]
137
        if isinstance(roles_read, str):
138
            roles_read = [roles_read]
139
        if isinstance(roles_list, str):
140
            roles_list = [roles_list]
141
142
        if has_owner:
143
            roles_write.append("owner")
144
            roles_read.append("owner")
145
            roles_list.append("owner")
146
147
        base_schema["roles_create"] = roles_create
148
        base_schema["properties"].update(
149
            {
150
                "perms": {
151
                    "id": "#perms",
152
                    "type": "object",
153
                    "name": "perms",
154
                    "properties": {
155
                        "write": {
156
                            "type": "array",
157
                            "default": roles_write,
158
                            "items": {"type": "string"},
159
                        },
160
                        "read": {
161
                            "type": "array",
162
                            "default": roles_read,
163
                            "items": {"type": "string"},
164
                        },
165
                        "list": {
166
                            "type": "array",
167
                            "default": roles_list,
168
                            "items": {"type": "string"},
169
                        },
170
                    },
171
                    "default": {},
172
                    "x-schema-form": {"condition": "false"},
173
                },
174
                "name": {"type": "string", "description": "Name of " + name},
175
            }
176
        )
177
178
        if has_owner:
179
            # TODO: Schema should allow specification of non-local owners as
180
            #  well as special accounts like admin or even system perhaps
181
            # base_schema['required'] = base_schema.get('required', [])
182
            # base_schema['required'].append('owner')
183
            base_schema["properties"].update(
184
                {"owner": uuid_object(title="Unique Owner ID", display=hide_owner)}
185
            )
186
    else:
187
        base_schema["no_perms"] = True
188
189
    if not no_color:
190
        base_schema["properties"].update(
191
            {"color": {"type": "string", "format": "colorpicker"}}
192
        )
193
194
    # TODO: Using this causes all sorts of (obvious) problems with the object
195
    # manager
196
    if has_uuid:
197
        base_schema["properties"].update(
198
            {"uuid": uuid_object(title="Unique " + name + " ID", display=False)}
199
        )
200
        base_schema["required"] = ["uuid"]
201
202
    return base_schema
203
204
205
def sql_object(*args, **kwargs):
206
    """Generates a basic SQL object with RBAC properties"""
207
208
    base_schema = base_object(*args, **kwargs)
209
210
    base_schema["class-properties"] = base_schema["properties"]["perms"]
211
    del base_schema["properties"]["perms"]
212
213
    base_schema["sql"] = True
214
215
    return base_schema
216
217
218
def language_field():
219
    schema = {
220
        "type": "string",
221
        "enum": all_languages(),
222
        "title": "Language",
223
        "description": "Select a language",
224
    }
225
226
    return schema
227