From b8ab55607ebbe2d4f7bf50926ac92d73ead2c828 Mon Sep 17 00:00:00 2001
From: Emmanuel Cazenave <ecazenave@entrouvert.com>
Date: Wed, 9 Jan 2019 17:25:01 +0100
Subject: [PATCH] planitech: create users under the hood (#29514)

---
 functests/planitech/test_planitech.py         | 11 ++-
 .../planitech/migrations/0003_pairing.py      | 30 +++++++
 passerelle/contrib/planitech/models.py        | 51 +++++++++++-
 tests/test_planitech.py                       | 83 ++++++++++++++++---
 4 files changed, 160 insertions(+), 15 deletions(-)
 create mode 100644 passerelle/contrib/planitech/migrations/0003_pairing.py

diff --git a/functests/planitech/test_planitech.py b/functests/planitech/test_planitech.py
index 0e8128a..5e26bd2 100644
--- a/functests/planitech/test_planitech.py
+++ b/functests/planitech/test_planitech.py
@@ -1,3 +1,4 @@
+import pprint
 import random
 import urllib
 
@@ -50,12 +51,20 @@ def test_main(conn):
     # create reservation
     params = {
         'date': chosen_date, 'start_time': '10:00', 'end_time': '11:00',
-        'place_id': place, 'price': 200
+        'place_id': place, 'price': 200, 'name_id': 'john-doe',
+        'first_name': 'jon', 'last_name': 'doe',
+        'email': 'jon.doe@localhost'
     }
+    print('Create reservation parameters \n')
+    pprint.pprint(params)
+    print('\n')
     url = conn + '/createreservation'
     resp = requests.post(url, json=params)
     resp.raise_for_status()
     res = resp.json()
+    print('Create reservation response \n')
+    pprint.pprint(res)
+    print('\n')
     assert res['err'] == 0
     data = res['data']
     assert 'reservation_id' in data
diff --git a/passerelle/contrib/planitech/migrations/0003_pairing.py b/passerelle/contrib/planitech/migrations/0003_pairing.py
new file mode 100644
index 0000000..d800e9b
--- /dev/null
+++ b/passerelle/contrib/planitech/migrations/0003_pairing.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.17 on 2019-01-10 11:08
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('planitech', '0002_planitechconnector_custom_fields'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Pairing',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name_id', models.CharField(max_length=256)),
+                ('external_id', models.CharField(max_length=256)),
+                ('created', models.DateTimeField(auto_now_add=True)),
+                ('resource', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='planitech.PlanitechConnector')),
+            ],
+        ),
+        migrations.AlterUniqueTogether(
+            name='pairing',
+            unique_together=set([('resource', 'name_id'), ('resource', 'external_id')]),
+        ),
+    ]
diff --git a/passerelle/contrib/planitech/models.py b/passerelle/contrib/planitech/models.py
index aa1dcc8..7de2d2a 100644
--- a/passerelle/contrib/planitech/models.py
+++ b/passerelle/contrib/planitech/models.py
@@ -19,9 +19,10 @@ import hashlib
 import json
 import re
 import urlparse
+import uuid
 
 from django.core.cache import cache
-from django.db import models
+from django.db import models, transaction
 from django.utils import dateformat
 from django.utils import dateparse
 from django.utils.translation import ugettext_lazy as _
@@ -64,6 +65,26 @@ CREATE_RESERVATION_SCHEMA = {
             "description": "Price",
             "type": "number",
             "required": True
+        },
+        "name_id": {
+            "description": "Publik user nameID",
+            "type": "string",
+            "required": True
+        },
+        "first_name": {
+            "description": "First name",
+            "type": "string",
+            "required": True
+        },
+        "last_name": {
+            "description": "Last name",
+            "type": "string",
+            "required": True
+        },
+        "email": {
+            "description": "Email",
+            "type": "string",
+            "required": True
         }
     }
 }
@@ -278,9 +299,24 @@ class PlanitechConnector(BaseResource):
         end_datetime = combine_date_time(post_data['date'], post_data['end_time'])
         request_date = datetime.now()
 
+        with transaction.atomic():
+            pairing, created = Pairing.objects.get_or_create(
+                resource=self, name_id=post_data['name_id'],
+                defaults={'external_id': uuid.uuid4().get_hex()})
+            if created:
+                params = {
+                    "externalUserIdentifier": pairing.external_id,
+                    "name": post_data['last_name'],
+                    "firstName": post_data['first_name'],
+                    "mail": post_data['email']
+                }
+                data = self._call_planitech(self.requests.post, 'createPerson', params)
+                if data.get('creationStatus') != 'OK':
+                    raise APIError("Person creation failed: %s" % data.get('creationStatus'))
+
         params = {
             "activityID": mste.Uint32(6),  # relaxation FIXME
-            "contractorExternalIdentifier": "test-entrouvert",  # FIXME
+            "contractorExternalIdentifier": pairing.external_id,
             "end": end_datetime,
             "isWeekly": False,
             "object": "reservation  test entouvert",  # FIXME
@@ -513,3 +549,14 @@ class PlanitechConnector(BaseResource):
                 'raw_data': data
             }
         }
+
+
+class Pairing(models.Model):
+
+    class Meta:
+        unique_together = (('resource', 'name_id'), ('resource', 'external_id'),)
+
+    resource = models.ForeignKey(PlanitechConnector)
+    name_id = models.CharField(blank=False, max_length=256)
+    external_id = models.CharField(blank=False, max_length=256)
+    created = models.DateTimeField(auto_now_add=True)
diff --git a/tests/test_planitech.py b/tests/test_planitech.py
index 5abd230..2a09b07 100644
--- a/tests/test_planitech.py
+++ b/tests/test_planitech.py
@@ -9,7 +9,7 @@ import requests
 
 from passerelle.base.models import ApiUser, AccessRight
 from passerelle.contrib.planitech import mste
-from passerelle.contrib.planitech.models import PlanitechConnector
+from passerelle.contrib.planitech.models import PlanitechConnector, Pairing
 from passerelle.utils.jsonresponse import APIError
 
 
@@ -141,11 +141,21 @@ def test_call_planitech(connector, monkeypatch):
 
 
 def test_create_reservation(app, connector, monkeypatch):
-    mock_call_planitech = mock_planitech(
-        monkeypatch, return_value={
+    side_effect = [
+        {
+            'creationStatus': 'OK',
+            'externalUserIdentifier': '22b9c0d91fdc4f379d1356a4aaa9d38b',
+            'requestDate': datetime(2019, 1, 9, 15, 41),
+            'requestName': 'createPerson',
+            'responseDate': datetime(2019, 1, 9, 15, 41),
+            'userIdentifier': 13826.0
+        },
+        {
             'creationStatus': 'OK',
             'reservationIdentifier': 1
-        })
+        }
+    ]
+    mock_call_planitech = mock_planitech(monkeypatch, side_effect=side_effect)
     response = app.post_json(
         '/planitech/slug-planitech/createreservation',
         params={
@@ -153,17 +163,57 @@ def test_create_reservation(app, connector, monkeypatch):
             'start_time': '10:00',
             'end_time': '11:00',
             'place_id': 1,
-            'price': 10
+            'price': 10,
+            'name_id': 'john-doe',
+            'first_name': 'jon',
+            'last_name': 'doe',
+            'email': 'jon.doe@localhost'
         }
     )
     json_resp = response.json
     assert json_resp['err'] == 0
     assert json_resp['data']['reservation_id'] == 1
-    call_params = mock_call_planitech.call_args[0][2]
-    assert call_params['start'] == datetime(2018, 11, 11, 10, 0)
-    assert call_params['end'] == datetime(2018, 11, 11, 11, 0)
-    assert call_params['places'] == [1]
-    assert call_params['price'] == 10
+
+    person_args = mock_call_planitech.call_args_list[0][0]
+    assert person_args[1] == 'createPerson'
+    person_args = person_args[2]
+    assert person_args['mail'] == 'jon.doe@localhost'
+    assert person_args['firstName'] == 'jon'
+    assert person_args['name'] == 'doe'
+    external_id = person_args['externalUserIdentifier']
+    assert Pairing.objects.get(resource=connector, name_id='john-doe').external_id == external_id
+
+    reservation_args = mock_call_planitech.call_args_list[1][0]
+    assert reservation_args[1] == 'createReservation'
+    reservation_args = reservation_args[2]
+    assert reservation_args['start'] == datetime(2018, 11, 11, 10, 0)
+    assert reservation_args['end'] == datetime(2018, 11, 11, 11, 0)
+    assert reservation_args['places'] == [1]
+    assert reservation_args['price'] == 10
+
+    # Second reservation for same user : no planitech person created
+    mock_call_planitech = mock_planitech(monkeypatch, return_value=side_effect[1])
+    response = app.post_json(
+        '/planitech/slug-planitech/createreservation',
+        params={
+            'date': '2018-11-11',
+            'start_time': '10:00',
+            'end_time': '11:00',
+            'place_id': 1,
+            'price': 10,
+            'name_id': 'john-doe',
+            'first_name': 'jon',
+            'last_name': 'doe',
+            'email': 'jon.doe@localhost'
+        }
+    )
+    json_resp = response.json
+    assert json_resp['err'] == 0
+    assert json_resp['data']['reservation_id'] == 1
+
+    reservation_args = mock_call_planitech.call_args_list[0][0]
+    assert reservation_args[1] == 'createReservation'
+    assert Pairing.objects.count() == 1
 
     # Create reservation failed
     mock_call_planitech = mock_planitech(
@@ -178,7 +228,12 @@ def test_create_reservation(app, connector, monkeypatch):
             'start_time': '10:00',
             'end_time': '11:00',
             'place_id': 1,
-            'price': 10
+            'price': 10,
+            'name_id': 'john-doe',
+            'first_name': 'jon',
+            'last_name': 'doe',
+            'email': 'jon.doe@localhost'
+
         }
     )
     json_resp = response.json
@@ -197,7 +252,11 @@ def test_create_reservation(app, connector, monkeypatch):
             'start_time': '10:00',
             'end_time': '11:00',
             'place_id': 1,
-            'price': 10
+            'price': 10,
+            'name_id': 'john-doe',
+            'first_name': 'jon',
+            'last_name': 'doe',
+            'email': 'jon.doe@localhost'
         }
     )
     json_resp = response.json
-- 
2.20.1

