From 8714ab7d9955191802829a7ade3e80949fb8c3d7 Mon Sep 17 00:00:00 2001
From: Thomas NOEL <tnoel@entrouvert.com>
Date: Fri, 24 May 2019 16:07:57 +0200
Subject: [PATCH] solis: add links parameter to retreive rsa _links information
 (#33377)

---
 passerelle/apps/solis/models.py | 48 ++++++++++++++++++++++--
 tests/test_solis.py             | 65 ++++++++++++++++++++++++++++++++-
 2 files changed, 107 insertions(+), 6 deletions(-)

diff --git a/passerelle/apps/solis/models.py b/passerelle/apps/solis/models.py
index 933d71f5..c688d58c 100644
--- a/passerelle/apps/solis/models.py
+++ b/passerelle/apps/solis/models.py
@@ -15,12 +15,14 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import base64
+import copy
 import json
 import re
 import unicodedata
 
 from django.db import models
 from django.template.loader import get_template
+from django.utils.encoding import force_text
 from django.utils.translation import ugettext_lazy as _
 from django.utils.http import urlencode
 
@@ -454,8 +456,23 @@ class Solis(BaseResource):
         })
         return response.get('token')
 
+    def rsa_get_link_content(self, link):
+        '''returns content of a '_links' entry in a grsa referential'''
+        if (not isinstance(link, dict) or not link.get('href')
+                or not link['href'].startswith(self.service_url)):
+            return None
+        endpoint = link['href'][len(self.service_url):]
+        try:
+            return self.request(endpoint)
+        except APIError as e:  # do not raise on linked informations
+            return {
+                'err': 1,
+                'err_class': e.__class__.__name__,
+                'err_desc': force_text(e)
+            }
+
     def rsa_get_information(self, information, user_id=None, code=None, token=None,
-                            index='search'):
+                            index='search', links=None):
         # simulate "individu" referential: get user details from civi/individu/user_id
         if information == 'individu':
             if not user_id:
@@ -473,7 +490,26 @@ class Solis(BaseResource):
         if index.startswith('search'):  # it can be "search/next" in rdvs referential
             args = [('indexIndividu', user_id)] + args
         endpoint += '?' + urlencode(args)
-        return self.request(endpoint)
+
+        information = self.request(endpoint)
+
+        if isinstance(information, dict) and '_links' in information:
+            # return links in non-underscored key, usable in Django template
+            information['rsa_links'] = copy.deepcopy(information['_links'])
+
+            if links is not None:
+                if not links.strip():  # get all links
+                    links = information['rsa_links'].keys()
+                else:
+                    links = [link.strip() for link in links.split(',') if link.strip()]
+                    links = [link for link in links if link in information['rsa_links']
+                             and information['rsa_links'][link].get('href')]
+                for link in links:
+                    content = self.rsa_get_link_content(information['rsa_links'][link])
+                    if content is not None:
+                        information['rsa_links'][link]['content'] = content
+
+        return information
 
     @endpoint(name='rsa-link', methods=['post'], perm='can_access',
               description=_('Create link between name_id and '
@@ -549,17 +585,21 @@ class Solis(BaseResource):
                   },
                   'index': {
                       'description': _('get a specific item, if applicable'),
+                  },
+                  'links': {
+                      'description': _('get linked informations (comma separated list, empty for all)'),
+                      'example_value': 'etatCivil,conjoint',
                   }
               })
     def rsa_user_info(self, request, name_id, user_id, information='individu',
-                      index='search'):
+                      index='search', links=None):
         try:
             link = SolisRSALink.objects.get(resource=self, name_id=name_id, user_id=user_id)
         except SolisRSALink.DoesNotExist:
             raise APIError('unknown link')
         response = self.rsa_get_information(information=information,
                                             user_id=user_id, code=link.code,
-                                            index=index)
+                                            index=index, links=links)
         if information == 'individu':
             text = get_template(self.text_template_name_rsa).render(response).strip()
             if text != link.text:
diff --git a/tests/test_solis.py b/tests/test_solis.py
index f2668d35..e61c02e4 100644
--- a/tests/test_solis.py
+++ b/tests/test_solis.py
@@ -47,10 +47,10 @@ RSAALLOCATAIRES = '''{
   "oriente": true,
   "_links": {
     "etatCivil": {
-      "href": "http://solis.infodb.fr/solisapi-rsa/referentiels/civi/individu/4242/"
+      "href": "https://solis.example.net/solisapi/referentiels/civi/individu/4242/"
     },
     "conjoint": {
-      "href": "http://solis.infodb.fr/solisapi-rsa/referentiels/civi/individu/4273/"
+      "href": "https://solis.example.net/solisapi/referentiels/civi/individu/4273/"
     }
   }
 }'''
@@ -830,6 +830,67 @@ def test_solis_rsa_link_infos_unlink(app, solis):
                 assert resp.json['err_desc'] == 'unknown link'
                 assert resp.json['data'] is None
 
+    # get referential for a linked user with links
+    with mock.patch('passerelle.utils.Request.get') as requests_get:
+        with mock.patch('passerelle.utils.Request.post') as requests_post:
+            requests_post.return_value = utils.FakedResponse(content=RSATOKEN, status_code=200)
+            endpoint_base = utils.generic_endpoint_url('solis', 'rsa-user-info', slug=solis.slug)
+
+            requests_get.side_effect = [
+                utils.FakedResponse(status_code=200, content=RSAALLOCATAIRES),  # base info
+                utils.FakedResponse(status_code=200, content=CIVI_INDIVIDU),    # link 1
+                utils.FakedResponse(status_code=200, content=CIVI_INDIVIDU)]    # link 2
+            endpoint = endpoint_base + '?name_id=%s&user_id=4242&information=allocataires&links' % NAMEID
+            resp = app.get(endpoint, status=200)
+            assert requests_post.call_count == 1  # get a token
+            assert requests_get.call_count == 3   # get informations + two links
+            assert resp.json['err'] == 0
+            assert resp.json['data']['rsa_links']['etatCivil']['content']['index'] == 4273
+            assert resp.json['data']['rsa_links']['conjoint']['content']['index'] == 4273
+
+            # get only conjoint
+            requests_post.reset_mock()
+            requests_get.reset_mock()
+            requests_get.side_effect = [
+                utils.FakedResponse(status_code=200, content=RSAALLOCATAIRES),  # base info
+                utils.FakedResponse(status_code=200, content=CIVI_INDIVIDU)]    # link 1
+            endpoint = endpoint_base + '?name_id=%s&user_id=4242&information=allocataires&links=conjoint, ,xx,' % NAMEID
+            resp = app.get(endpoint, status=200)
+            assert requests_post.call_count == 1  # get a token
+            assert requests_get.call_count == 2   # get informations + conjoint
+            assert resp.json['err'] == 0
+            assert resp.json['data']['rsa_links']['conjoint']['content']['index'] == 4273
+            assert 'content' not in resp.json['data']['rsa_links']['etatCivil']
+
+            # error on link retreival
+            requests_post.reset_mock()
+            requests_get.reset_mock()
+            requests_get.side_effect = [
+                utils.FakedResponse(status_code=200, content=RSAALLOCATAIRES),   # base info
+                utils.FakedResponse(status_code=404, content='{"foo": "bar"}'),  # link 1
+                utils.FakedResponse(status_code=500, content='boom')]            # link 2
+            endpoint = endpoint_base + '?name_id=%s&user_id=4242&information=allocataires&links' % NAMEID
+            resp = app.get(endpoint, status=200)
+            assert requests_post.call_count == 1  # get a token
+            assert requests_get.call_count == 3   # get informations + two links
+            assert resp.json['err'] == 0
+            assert resp.json['data']['rsa_links']['etatCivil']['content']['err'] == 1
+            assert resp.json['data']['rsa_links']['conjoint']['content']['err'] == 1
+
+            # bad links, do nothing
+            for content in (RSAALLOCATAIRES.replace('https:', 'http:'),
+                            RSAALLOCATAIRES.replace('href', 'xxxx')):
+                requests_post.reset_mock()
+                requests_get.reset_mock()
+                requests_get.side_effect = [utils.FakedResponse(status_code=200, content=content)]
+                endpoint = endpoint_base + '?name_id=%s&user_id=4242&information=allocataires&links' % NAMEID
+                resp = app.get(endpoint, status=200)
+                assert requests_post.call_count == 1  # get a token
+                assert requests_get.call_count == 1   # get informations only, not links
+                assert resp.json['err'] == 0
+                assert 'content' not in resp.json['data']['rsa_links']['conjoint']
+                assert 'content' not in resp.json['data']['rsa_links']['etatCivil']
+
     # unlink
     endpoint = utils.generic_endpoint_url('solis', 'rsa-unlink', slug=solis.slug)
     for bad_params in ({}, {'user_id': '4273'}, {'name_id': NAMEID}):
-- 
2.20.1

