Projet

Général

Profil

0001-toulouse_smart-do-not-crash-on-unknown-property-type.patch

Nicolas Roche, 26 janvier 2022 17:20

Télécharger (6,12 ko)

Voir les différences:

Subject: [PATCH 1/2] toulouse_smart: do not crash on unknown property type
 (#60989)

 passerelle/contrib/toulouse_smart/models.py |  5 ++++
 tests/test_toulouse_smart.py                | 29 +++++++++++++++++++++
 2 files changed, 34 insertions(+)
passerelle/contrib/toulouse_smart/models.py
172 172
            block = post_data['fields']['%s_raw' % wcs_block_varname][0]
173 173
        except (KeyError, TypeError):
174 174
            block = {}
175 175
        data = {}
176 176
        cast = {'string': str, 'int': int, 'boolean': bool, 'item': str}
177 177
        for prop in intervention_type.get('properties') or []:
178 178
            varname = slugify(prop['name']).replace('-', '_')
179 179
            if block.get(varname):
180
                if 'type' not in prop:
181
                    raise APIError(
182
                        'unknwon type for the %s/%s property' % (intervention_type['name'], prop['name']),
183
                        http_status=400,
184
                    )
180 185
                try:
181 186
                    data[prop['name']] = cast[prop['type']](block[varname])
182 187
                except ValueError:
183 188
                    raise APIError(
184 189
                        "cannot cast '%s' field to %s : '%s'" % (varname, cast[prop['type']], block[varname]),
185 190
                        http_status=400,
186 191
                    )
187 192
            elif prop['required']:
tests/test_toulouse_smart.py
151 151
              </restrictedValues>
152 152
           </properties>
153 153
           <properties>
154 154
              <name>FIELD2</name>
155 155
              <displayName>Champ 2</displayName>
156 156
              <type>int</type>
157 157
              <required>true</required>
158 158
           </properties>
159
           <properties>
160
              <name>FIELD3</name>
161
              <displayName>Champ 3</displayName>
162
           </properties>
159 163
       </properties>
160 164
   </item>
161 165
   <item>
162 166
       <id>0002</id>
163 167
       <name>empty</name>
164 168
   </item>
165 169
</List>'''.encode()
166 170

  
......
177 181
                    'name': 'TYPE-OBJET',
178 182
                    'displayName': 'Champ 1',
179 183
                    'required': False,
180 184
                    'type': 'item',
181 185
                    'defaultValue': 'Ne sait pas',
182 186
                    'restrictedValues': ['Candélabre', 'Mât', 'Ne sait pas'],
183 187
                },
184 188
                {'name': 'FIELD2', 'displayName': 'Champ 2', 'required': True, 'type': 'int'},
189
                {'name': 'FIELD3', 'displayName': 'Champ 3', 'required': False},
185 190
            ],
186 191
        },
187 192
        {
188 193
            'id': '0002',
189 194
            'name': 'empty',
190 195
            'order': 2,
191 196
        },
192 197
    ]
......
217 222
def test_manage_intervention_types(app, smart, admin_user):
218 223
    login(app)
219 224
    resp = app.get('/manage' + URL + 'type-intervention/')
220 225
    assert [[td.text for td in tr.cssselect('td,th')] for tr in resp.pyquery('tr')] == [
221 226
        ["Nom du type d'intervention", 'Nom', 'Type', 'Requis', 'Valeur par défaut'],
222 227
        ['1 - coin'],
223 228
        [None, 'TYPE-OBJET', 'item («Candélabre», «Mât», «Ne sait pas»)', '✘', 'Ne sait pas'],
224 229
        [None, 'FIELD2', 'int', '✔', None],
230
        [None, 'FIELD3', None, '✘', None],
225 231
        ['2 - empty'],
226 232
    ]
227 233
    resp = resp.click('Export to blocks')
228 234
    with zipfile.ZipFile(io.BytesIO(resp.body)) as zip_file:
229 235
        assert zip_file.namelist() == ['block-coin.wcs']
230 236
        with zip_file.open('block-coin.wcs') as fd:
231 237
            content = ET.tostring(ET.fromstring(fd.read()), pretty_print=True).decode()
232 238
            assert (
......
260 266
      <display_locations>
261 267
        <display_location>validation</display_location>
262 268
        <display_location>summary</display_location>
263 269
      </display_locations>
264 270
      <validation>
265 271
        <type>digits</type>
266 272
      </validation>
267 273
    </field>
274
    <field>
275
      <id>66c8a74e-cd26-eb34-2bec-9c0c4ec43841</id>
276
      <label>Champ 3</label>
277
      <type>string</type>
278
      <required>False</required>
279
      <varname>field3</varname>
280
      <display_locations>
281
        <display_location>validation</display_location>
282
        <display_location>summary</display_location>
283
      </display_locations>
284
    </field>
268 285
  </fields>
269 286
</block>
270 287
'''
271 288
            )
272 289

  
273 290

  
274 291
INTERVENTION_ID = json.loads(get_json_file('create_intervention'))['id']
275 292

  
......
482 499
def test_create_intervention_cast_error(app, smart):
483 500
    payload = deepcopy(CREATE_INTERVENTION_PAYLOAD)
484 501
    payload['fields']['coin_raw'][0]['field2'] = 'not-an-integer'
485 502
    resp = app.post_json(URL + 'create-intervention/', params=payload, status=400)
486 503
    assert resp.json['err']
487 504
    assert "cannot cast 'field2' field to <class 'int'>" in resp.json['err_desc']
488 505

  
489 506

  
507
@mock_response(
508
    ['/v1/type-intervention', None, INTERVENTION_TYPES],
509
)
510
@mock.patch("django.db.models.fields.UUIDField.get_default", return_value=UUID)
511
def test_create_intervention_no_type_to_cast(mocked_uuid4, app, smart):
512
    payload = deepcopy(CREATE_INTERVENTION_PAYLOAD)
513
    payload['fields']['coin_raw'][0]['field3'] = 'plop value'
514
    resp = app.post_json(URL + 'create-intervention/', params=payload, status=400)
515
    assert resp.json['err']
516
    assert resp.json['err_desc'] == 'unknwon type for the coin/FIELD3 property'
517

  
518

  
490 519
@mock_response(
491 520
    ['/v1/type-intervention', None, INTERVENTION_TYPES],
492 521
)
493 522
def test_create_intervention_missing_value(app, smart):
494 523
    field_payload = {
495 524
        'coin_raw': [
496 525
            {
497 526
                'type_objet': 'Candélabre',
498
-