• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

chiefonboarding / ChiefOnboarding / 6713327791

31 Oct 2023 10:53PM UTC coverage: 93.672% (+0.01%) from 93.66%
6713327791

Pull #383

github

web-flow
Merge 253ea48ef into fb838f71e
Pull Request #383: Add offboarding sequences

6262 of 6685 relevant lines covered (93.67%)

0.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.85
back/admin/integrations/forms.py
1
import json
1✔
2

3
from crispy_forms.helper import FormHelper
1✔
4
from crispy_forms.layout import Div, Field, Layout, HTML
1✔
5
from django import forms
1✔
6
from django.contrib.auth import get_user_model
1✔
7
from django.core.exceptions import ValidationError
1✔
8
from django.utils.translation import gettext_lazy as _
1✔
9

10
from admin.integrations.utils import get_value_from_notation
1✔
11
from admin.integrations.models import Integration
1✔
12
from admin.sequences.models import IntegrationConfig
1✔
13

14

15
class IntegrationConfigForm(forms.ModelForm):
1✔
16
    def _expected_example(self, form_item):
1✔
17
        def _add_items(form_item):
1✔
18
            items = []
1✔
19
            # Add two example items
20
            for item in range(2):
1✔
21
                items.append(
1✔
22
                    {
23
                        form_item.get("choice_value", "id"): item,
24
                        form_item.get("choice_name", "name"): f"name {item}",
25
                    }
26
                )
27
            return items
1✔
28

29
        inner = form_item.get("data_from", "")
1✔
30
        if inner == "":
1✔
31
            return _add_items(form_item)
1✔
32

33
        # This is pretty ugly, but we are building a json string first
34
        # and then convert it to a real json object to avoid nested loops
35
        notations = inner.split(".")
1✔
36
        stringified_json = "{"
1✔
37
        for idx, notation in enumerate(notations):
1✔
38
            stringified_json += f'"{notation}":'
1✔
39
            if idx + 1 == len(notations):
1✔
40
                stringified_json += json.dumps(_add_items(form_item))
1✔
41
                stringified_json += "}" * len(notations)
1✔
42
            else:
43
                stringified_json += "{"
1✔
44

45
        return json.loads(stringified_json)
1✔
46

47
    def __init__(self, *args, **kwargs):
1✔
48
        super().__init__(*args, **kwargs)
1✔
49
        integration = Integration.objects.get(id=self.instance.id)
1✔
50
        form = self.instance.manifest["form"]
1✔
51
        self.helper = FormHelper()
1✔
52
        self.helper.form_tag = False
1✔
53
        self.error = None
1✔
54
        for item in form:
1✔
55
            if item["type"] == "input":
1✔
56
                self.fields[item["id"]] = forms.CharField(
1✔
57
                    label=item["name"],
58
                    required=False,
59
                )
60

61
            if item["type"] in ["choice", "multiple_choice"]:
1✔
62
                # If there is a url to fetch the items from then do so
63
                if "url" in item:
1✔
64
                    success, response = integration.run_request(item)
1✔
65
                    if not success:
1✔
66
                        self.error = response
×
67
                        return
×
68

69
                    option_data = response.json()
1✔
70
                else:
71
                    # No url, so get the static items
72
                    option_data = item["items"]
1✔
73

74
                # Can we select one or multiple?
75
                if item["type"] == "choice":
1✔
76
                    field = forms.ChoiceField
1✔
77
                else:
78
                    field = forms.MultipleChoiceField
×
79

80
                try:
1✔
81
                    self.fields[item["id"]] = field(
1✔
82
                        label=item["name"],
83
                        widget=forms.CheckboxSelectMultiple
84
                        if item["type"] == "multiple_choice"
85
                        else forms.Select,
86
                        choices=[
87
                            (
88
                                get_value_from_notation(
89
                                    item.get("choice_value", "id"), x
90
                                ),
91
                                get_value_from_notation(
92
                                    item.get("choice_name", "name"), x
93
                                ),
94
                            )
95
                            for x in get_value_from_notation(
96
                                item.get("data_from", ""), option_data
97
                            )
98
                        ],
99
                        required=False,
100
                    )
101
                except Exception:
1✔
102
                    expected = self._expected_example(item)
1✔
103

104
                    self.error = (
1✔
105
                        f"Form item ({item['name']}) could not be rendered. Format "
106
                        "was different than expected.<br><h2>Expected format:"
107
                        f"</h2><pre>{json.dumps(expected, indent=4)}</pre><br><h2>"
108
                        "Got from server:</h2><pre>"
109
                        f"{json.dumps(option_data, indent=4)}</pre>"
110
                    )
111
                    break
1✔
112

113
    class Meta:
1✔
114
        model = Integration
1✔
115
        fields = ()
1✔
116

117

118
class ManualIntegrationConfigForm(forms.ModelForm):
1✔
119
    def __init__(self, *args, **kwargs):
1✔
120
        super().__init__(*args, **kwargs)
1✔
121
        self.helper = FormHelper()
1✔
122
        self.helper.form_tag = False
1✔
123

124
        hide_assigned_to = "d-none"
1✔
125
        if (
1✔
126
            self.instance is not None
127
            and self.instance.person_type == IntegrationConfig.PersonType.CUSTOM
128
        ):
129
            hide_assigned_to = ""
×
130

131
        self.helper.layout = Layout(
1✔
132
            HTML(
133
                "<p>"
134
                + _(
135
                    "This is a manual integration, you will have to assign someone "
136
                    "to create/remove it for this person."
137
                )
138
                + "</p>"
139
            ),
140
            Div(
141
                Field("name"),
142
                Field("person_type"),
143
                Div(
144
                    Field("assigned_to"),
145
                    css_class=hide_assigned_to,
146
                ),
147
            ),
148
        )
149

150
    class Meta:
1✔
151
        model = IntegrationConfig
1✔
152
        fields = ("person_type", "assigned_to")
1✔
153

154
    def clean(self):
1✔
155
        cleaned_data = super().clean()
1✔
156
        person_type = cleaned_data.get("person_type", None)
1✔
157
        assigned_to = cleaned_data.get("assigned_to", None)
1✔
158
        if person_type == IntegrationConfig.PersonType.CUSTOM and assigned_to is None:
1✔
159
            raise ValidationError(
1✔
160
                _("You must select someone if you want someone custom")
161
            )
162
        return cleaned_data
1✔
163

164

165
# Credits: https://stackoverflow.com/a/72256767
166
# Removed the sort options
167
class PrettyJSONEncoder(json.JSONEncoder):
1✔
168
    def __init__(self, *args, indent, **kwargs):
1✔
169
        super().__init__(*args, indent=4, **kwargs)
1✔
170

171

172
class IntegrationForm(forms.ModelForm):
1✔
173
    """Form used to register a new integration through the settings"""
174

175
    manifest = forms.JSONField(encoder=PrettyJSONEncoder, required=False, initial=dict)
1✔
176

177
    class Meta:
1✔
178
        model = Integration
1✔
179
        fields = ("name", "manifest_type", "manifest")
1✔
180

181
    def __init__(self, *args, **kwargs):
1✔
182
        super().__init__(*args, **kwargs)
1✔
183
        self.fields["manifest_type"].required = True
1✔
184

185
        # make manifest not required when manual user provisioning
186
        if (
1✔
187
            self.data.get("manifest_type", "")
188
            != str(Integration.ManifestType.MANUAL_USER_PROVISIONING)
189
            and self.instance.manifest_type
190
            != Integration.ManifestType.MANUAL_USER_PROVISIONING
191
        ):
192
            self.fields["manifest"].required = True
1✔
193

194
        if self.instance.id:
1✔
195
            # disable manifest_type when updating record
196
            self.fields["manifest_type"].disabled = True
1✔
197

198

199
class IntegrationExtraArgsForm(forms.ModelForm):
1✔
200
    def __init__(self, *args, **kwargs):
1✔
201
        super().__init__(*args, **kwargs)
1✔
202
        initial_data = self.instance.extra_args
1✔
203
        for item in self.instance.manifest["initial_data_form"]:
1✔
204
            self.fields[item["id"]] = forms.CharField(
1✔
205
                label=item["name"], help_text=item["description"]
206
            )
207
            # Check if item was already saved - load data back in form
208
            if item["id"] in initial_data:
1✔
209
                self.fields[item["id"]].initial = initial_data[item["id"]]
1✔
210
            # If field is secret field, then hide it - values are generated on the fly
211
            if "name" in item and item["name"] == "generate":
1✔
212
                self.fields[item["id"]].required = False
1✔
213
                self.fields[item["id"]].widget = forms.HiddenInput()
1✔
214

215
    def save(self):
1✔
216
        integration = self.instance
1✔
217
        integration.extra_args = self.cleaned_data
1✔
218
        integration.save()
1✔
219
        return integration
1✔
220

221
    class Meta:
1✔
222
        model = Integration
1✔
223
        fields = ()
1✔
224

225

226
class IntegrationExtraUserInfoForm(forms.ModelForm):
1✔
227
    def __init__(self, missing_info=None, *args, **kwargs):
1✔
228
        super().__init__(*args, **kwargs)
1✔
229
        if missing_info is None:
1✔
230
            missing_info = self.instance.missing_extra_info
1✔
231

232
        for item in missing_info:
1✔
233
            self.fields[item["id"]] = forms.CharField(
1✔
234
                label=item["name"], help_text=item["description"]
235
            )
236

237
    def save(self):
1✔
238
        user = self.instance
1✔
239
        user.extra_fields |= self.cleaned_data
1✔
240
        user.save()
1✔
241
        return user
1✔
242

243
    class Meta:
1✔
244
        model = get_user_model()
1✔
245
        fields = ()
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc