From 7e796e585d38133916ae96a606d0d109cbbaf3f4 Mon Sep 17 00:00:00 2001
From: Nicolas ROCHE <nroche@entrouvert.com>
Date: Sat, 16 Oct 2021 16:36:12 +0200
Subject: [PATCH 3/5] plone_restapi: add endpoint to get content type (#57904)

---
 passerelle/apps/plone_restapi/models.py       |  12 +
 .../data/plone_restapi/get_content_type.json  | 692 ++++++++++++++++++
 tests/test_plone_restapi.py                   |  17 +
 3 files changed, 721 insertions(+)
 create mode 100644 tests/data/plone_restapi/get_content_type.json

diff --git a/passerelle/apps/plone_restapi/models.py b/passerelle/apps/plone_restapi/models.py
index ef9aa4ad..3a3f531a 100644
--- a/passerelle/apps/plone_restapi/models.py
+++ b/passerelle/apps/plone_restapi/models.py
@@ -208,16 +208,28 @@ class PloneRestApi(BaseResource):
     )
     def get_content_types(self, request):
         response = self.request(uri='@types', method='GET')
         for record in response or []:
             self.adapt_record(record, '{{ title }}', id_key='PLONE_id')
             record['id'] = record['id'].split('/')[-1]
         return {'data': response or []}
 
+    @endpoint(
+        perm='can_access',
+        description=_('Get content type'),
+        parameters={
+            'id': {'description': _('Content type identifier'), 'example_value': 'imio.directory.Contact'}
+        },
+        display_order=2,
+    )
+    def get_content_type(self, request, id):
+        response = self.request(uri='@types', uid=id, method='GET')
+        return {'data': response}
+
     @endpoint(
         perm='can_access',
         description=_('Fetch'),
         parameters={
             'uri': {'description': _('Uri')},
             'uid': {'description': _('Uid')},
             'text_template': {'description': _('Text template')},
         },
diff --git a/tests/data/plone_restapi/get_content_type.json b/tests/data/plone_restapi/get_content_type.json
new file mode 100644
index 00000000..42e839d7
--- /dev/null
+++ b/tests/data/plone_restapi/get_content_type.json
@@ -0,0 +1,692 @@
+{
+  "fieldsets": [
+    {
+      "behavior": "plone",
+      "fields": [
+        "title",
+        "description",
+        "type",
+        "subtitle",
+        "logo",
+        "image",
+        "image_caption",
+        "geolocation"
+      ],
+      "id": "default",
+      "title": "G\u00e9n\u00e9ral"
+    },
+    {
+      "behavior": "plone",
+      "fields": [
+        "street",
+        "number",
+        "complement",
+        "zipcode",
+        "city",
+        "country"
+      ],
+      "id": "address",
+      "title": "Adresse"
+    },
+    {
+      "behavior": "plone",
+      "fields": [
+        "vat_number",
+        "phones",
+        "mails",
+        "urls"
+      ],
+      "id": "contact_informations",
+      "title": "Coordonn\u00e9es"
+    },
+    {
+      "behavior": "plone.dexterity.schema.generated",
+      "fields": [
+        "selected_entities",
+        "facilities",
+        "taxonomy_contact_category",
+        "topics",
+        "iam",
+        "subjects",
+        "language"
+      ],
+      "id": "categorization",
+      "title": "Cat\u00e9gorisation"
+    },
+    {
+      "behavior": "plone",
+      "fields": [
+        "schedule",
+        "multi_schedule"
+      ],
+      "id": "multischedule",
+      "title": "Horaire multiple"
+    },
+    {
+      "behavior": "plone",
+      "fields": [
+        "exceptional_closure"
+      ],
+      "id": "exceptionalclosure",
+      "title": "Fermeture exceptionnelle"
+    }
+  ],
+  "layouts": [
+    "view"
+  ],
+  "properties": {
+    "city": {
+      "behavior": "imio.directory.core.contents.contact.content.IContact",
+      "description": "",
+      "factory": "Text line (String)",
+      "title": "Commune",
+      "type": "string"
+    },
+    "complement": {
+      "behavior": "imio.directory.core.contents.contact.content.IContact",
+      "description": "",
+      "factory": "Text line (String)",
+      "title": "Compl\u00e9ment",
+      "type": "string"
+    },
+    "country": {
+      "behavior": "imio.directory.core.contents.contact.content.IContact",
+      "description": "",
+      "factory": "Choice",
+      "title": "Pays",
+      "type": "string",
+      "vocabulary": {
+        "@id": "https://annuaire.preprod.imio.be/@vocabularies/imio.smartweb.vocabulary.Countries"
+      }
+    },
+    "description": {
+      "behavior": "plone.basic",
+      "description": "Utilis\u00e9 dans les pages listant des \u00e9l\u00e9ments et les pages de r\u00e9sultats de recherche.",
+      "factory": "Text",
+      "maxLength": 700,
+      "title": "Description",
+      "type": "string",
+      "widget": "textarea"
+    },
+    "exceptional_closure": {
+      "additionalItems": true,
+      "behavior": "collective.schedulefield.behavior.IExceptionalClosureContent",
+      "description": "",
+      "factory": "Collection",
+      "items": {
+        "description": "",
+        "factory": "File",
+        "properties": {
+          "ExceptionalClosure.date": {
+            "description": "",
+            "factory": "Date",
+            "title": "Date",
+            "type": "string",
+            "widget": "date"
+          },
+          "ExceptionalClosure.title": {
+            "description": "",
+            "factory": "Text",
+            "title": "Titre",
+            "type": "string",
+            "widget": "textarea"
+          }
+        },
+        "title": "",
+        "type": "object"
+      },
+      "title": "Dates",
+      "type": "array",
+      "uniqueItems": false
+    },
+    "facilities": {
+      "additionalItems": true,
+      "behavior": "imio.directory.core.contents.contact.content.IContact",
+      "description": "Important! Ces cat\u00e9gories permettent de mettre en avant et de g\u00e9olocaliser certains services de base",
+      "factory": "List",
+      "items": {
+        "description": "",
+        "factory": "Choice",
+        "title": "",
+        "type": "string",
+        "vocabulary": {
+          "@id": "https://annuaire.preprod.imio.be/@vocabularies/imio.directory.vocabulary.Facilities"
+        }
+      },
+      "title": "Facilit\u00e9s",
+      "type": "array",
+      "uniqueItems": true
+    },
+    "geolocation": {
+      "behavior": "geolocatable",
+      "description": "Cliquez sur la carte pour s\u00e9lectionner une localisation, ou utilisez le champ texte pour rechercher par adresse.",
+      "factory": "File",
+      "properties": {
+        "geolocation.latitude": {
+          "description": "",
+          "factory": "Floating-point number",
+          "title": "Latitude",
+          "type": "number"
+        },
+        "geolocation.longitude": {
+          "description": "",
+          "factory": "Floating-point number",
+          "title": "Longitude",
+          "type": "number"
+        }
+      },
+      "title": "G\u00e9olocalisation",
+      "type": "object"
+    },
+    "iam": {
+      "additionalItems": true,
+      "behavior": "imio.smartweb.iam",
+      "description": "Important! Ces cat\u00e9gories sont utilis\u00e9es pour cr\u00e9er des listes accessibles via le menu navigation",
+      "factory": "List",
+      "items": {
+        "description": "",
+        "factory": "Choice",
+        "title": "",
+        "type": "string",
+        "vocabulary": {
+          "@id": "https://annuaire.preprod.imio.be/@vocabularies/imio.smartweb.vocabulary.IAm"
+        }
+      },
+      "title": "Je suis",
+      "type": "array",
+      "uniqueItems": true
+    },
+    "image": {
+      "behavior": "plone.leadimage",
+      "description": "",
+      "factory": "Image",
+      "properties": {
+        "image.contentType": {
+          "default": "",
+          "description": "The content type identifies the type of data.",
+          "factory": "Text line (String)",
+          "title": "Content Type",
+          "type": "string"
+        },
+        "image.data": {
+          "default": "",
+          "description": "The actual content of the object.",
+          "factory": "Text line (String)",
+          "title": "Data",
+          "type": "string"
+        },
+        "image.filename": {
+          "description": "",
+          "factory": "Text line (String)",
+          "title": "Filename",
+          "type": "string"
+        }
+      },
+      "title": "Image principale",
+      "type": "object"
+    },
+    "image_caption": {
+      "behavior": "plone.leadimage",
+      "description": "",
+      "factory": "Text line (String)",
+      "title": "L\u00e9gende de l'image principale",
+      "type": "string"
+    },
+    "language": {
+      "behavior": "plone.categorization",
+      "default": "fr",
+      "description": "",
+      "factory": "Choice",
+      "title": "Langue",
+      "type": "string",
+      "vocabulary": {
+        "@id": "https://annuaire.preprod.imio.be/@vocabularies/plone.app.vocabularies.SupportedContentLanguages"
+      }
+    },
+    "logo": {
+      "behavior": "imio.directory.core.contents.contact.content.IContact",
+      "description": "",
+      "factory": "Image",
+      "properties": {
+        "logo.contentType": {
+          "default": "",
+          "description": "The content type identifies the type of data.",
+          "factory": "Text line (String)",
+          "title": "Content Type",
+          "type": "string"
+        },
+        "logo.data": {
+          "default": "",
+          "description": "The actual content of the object.",
+          "factory": "Text line (String)",
+          "title": "Data",
+          "type": "string"
+        },
+        "logo.filename": {
+          "description": "",
+          "factory": "Text line (String)",
+          "title": "Filename",
+          "type": "string"
+        }
+      },
+      "title": "Logo",
+      "type": "object"
+    },
+    "mails": {
+      "additionalItems": true,
+      "behavior": "imio.directory.core.contents.contact.content.IContact",
+      "description": "",
+      "factory": "List",
+      "items": {
+        "description": "",
+        "factory": "File",
+        "properties": {
+          "label": {
+            "description": "",
+            "factory": "Text line (String)",
+            "title": "Intitul\u00e9 (Secr\u00e9tariat, Bureau de la direction, Ventes, ...)",
+            "type": "string"
+          },
+          "mail_address": {
+            "description": "",
+            "factory": "Email",
+            "title": "Adresse e-mail",
+            "type": "string",
+            "widget": "email"
+          },
+          "type": {
+            "description": "",
+            "factory": "Choice",
+            "title": "Type",
+            "type": "string",
+            "vocabulary": {
+              "@id": "https://annuaire.preprod.imio.be/@vocabularies/imio.directory.vocabulary.MailTypes"
+            }
+          }
+        },
+        "title": "Value",
+        "type": "object"
+      },
+      "title": "E-mails",
+      "type": "array",
+      "uniqueItems": false
+    },
+    "multi_schedule": {
+      "additionalItems": true,
+      "behavior": "collective.schedulefield.behavior.IMultiScheduledContent",
+      "description": "",
+      "factory": "Collection",
+      "items": {
+        "description": "",
+        "factory": "File",
+        "properties": {
+          "MultiSchedule.dates": {
+            "additionalItems": true,
+            "description": "",
+            "factory": "List",
+            "items": {
+              "description": "",
+              "factory": "File",
+              "properties": {
+                "DateRange.end_date": {
+                  "description": "",
+                  "factory": "Date",
+                  "title": "Date de fin",
+                  "type": "string",
+                  "widget": "date"
+                },
+                "DateRange.start_date": {
+                  "description": "",
+                  "factory": "Date",
+                  "title": "Date de d\u00e9but",
+                  "type": "string",
+                  "widget": "date"
+                }
+              },
+              "title": "",
+              "type": "object"
+            },
+            "title": "Dates",
+            "type": "array",
+            "uniqueItems": false
+          },
+          "MultiSchedule.schedule": {
+            "description": "",
+            "key_type": {
+              "additional": {},
+              "schema": {
+                "description": "",
+                "factory": "Text line (String)",
+                "title": "",
+                "type": "string"
+              }
+            },
+            "title": "Horaire",
+            "type": "dict",
+            "value_type": {
+              "additional": {
+                "key_type": {
+                  "additional": {},
+                  "schema": {
+                    "description": "",
+                    "factory": "Text line (String)",
+                    "title": "",
+                    "type": "string"
+                  }
+                },
+                "value_type": {
+                  "additional": {},
+                  "schema": {
+                    "description": "",
+                    "factory": "Text line (String)",
+                    "title": "",
+                    "type": "string"
+                  }
+                }
+              },
+              "schema": {
+                "description": "",
+                "key_type": {
+                  "additional": {},
+                  "schema": {
+                    "description": "",
+                    "factory": "Text line (String)",
+                    "title": "",
+                    "type": "string"
+                  }
+                },
+                "title": "",
+                "type": "dict",
+                "value_type": {
+                  "additional": {},
+                  "schema": {
+                    "description": "",
+                    "factory": "Text line (String)",
+                    "title": "",
+                    "type": "string"
+                  }
+                }
+              }
+            }
+          },
+          "MultiSchedule.title": {
+            "description": "",
+            "factory": "Text",
+            "title": "Titre",
+            "type": "string",
+            "widget": "textarea"
+          }
+        },
+        "title": "",
+        "type": "object"
+      },
+      "title": "Horaire multiple",
+      "type": "array",
+      "uniqueItems": false
+    },
+    "number": {
+      "behavior": "imio.directory.core.contents.contact.content.IContact",
+      "description": "",
+      "factory": "Text line (String)",
+      "title": "Num\u00e9ro",
+      "type": "string"
+    },
+    "phones": {
+      "additionalItems": true,
+      "behavior": "imio.directory.core.contents.contact.content.IContact",
+      "description": "",
+      "factory": "List",
+      "items": {
+        "description": "",
+        "factory": "File",
+        "properties": {
+          "label": {
+            "description": "",
+            "factory": "Text line (String)",
+            "title": "Intitul\u00e9 (direction, num\u00e9ro principal, ...)",
+            "type": "string"
+          },
+          "number": {
+            "description": "",
+            "factory": "Text line (String)",
+            "title": "Num\u00e9ro (format: +32475010203)",
+            "type": "string"
+          },
+          "type": {
+            "description": "",
+            "factory": "Choice",
+            "title": "Type",
+            "type": "string",
+            "vocabulary": {
+              "@id": "https://annuaire.preprod.imio.be/@vocabularies/imio.directory.vocabulary.PhoneTypes"
+            }
+          }
+        },
+        "title": "Value",
+        "type": "object"
+      },
+      "title": "T\u00e9l\u00e9phones",
+      "type": "array",
+      "uniqueItems": false
+    },
+    "schedule": {
+      "behavior": "collective.schedulefield.behavior.IMultiScheduledContent",
+      "description": "",
+      "key_type": {
+        "additional": {},
+        "schema": {
+          "description": "",
+          "factory": "Text line (String)",
+          "title": "",
+          "type": "string"
+        }
+      },
+      "title": "Horaire",
+      "type": "dict",
+      "value_type": {
+        "additional": {
+          "key_type": {
+            "additional": {},
+            "schema": {
+              "description": "",
+              "factory": "Text line (String)",
+              "title": "",
+              "type": "string"
+            }
+          },
+          "value_type": {
+            "additional": {},
+            "schema": {
+              "description": "",
+              "factory": "Text line (String)",
+              "title": "",
+              "type": "string"
+            }
+          }
+        },
+        "schema": {
+          "description": "",
+          "key_type": {
+            "additional": {},
+            "schema": {
+              "description": "",
+              "factory": "Text line (String)",
+              "title": "",
+              "type": "string"
+            }
+          },
+          "title": "",
+          "type": "dict",
+          "value_type": {
+            "additional": {},
+            "schema": {
+              "description": "",
+              "factory": "Text line (String)",
+              "title": "",
+              "type": "string"
+            }
+          }
+        }
+      }
+    },
+    "selected_entities": {
+      "additionalItems": true,
+      "behavior": "imio.directory.core.contents.contact.content.IContact",
+      "default": [],
+      "description": "S\u00e9lectionnez les entit\u00e9s dans lesquelles ce contact sera affich\u00e9. L'entit\u00e9 courante sera toujours s\u00e9lectionn\u00e9e.",
+      "factory": "List",
+      "items": {
+        "description": "",
+        "factory": "Choice",
+        "title": "",
+        "type": "string",
+        "vocabulary": {
+          "@id": "https://annuaire.preprod.imio.be/@vocabularies/imio.directory.vocabulary.EntitiesUIDs"
+        }
+      },
+      "title": "Entit\u00e9s concern\u00e9es",
+      "type": "array",
+      "uniqueItems": true
+    },
+    "street": {
+      "behavior": "imio.directory.core.contents.contact.content.IContact",
+      "description": "",
+      "factory": "Text line (String)",
+      "title": "Rue",
+      "type": "string"
+    },
+    "subjects": {
+      "additionalItems": true,
+      "behavior": "plone.categorization",
+      "description": "Les mots cl\u00e9s sont utilis\u00e9s couramment pour organiser le contenu de fa\u00e7on ad hoc.",
+      "factory": "Tuple",
+      "items": {
+        "description": "",
+        "factory": "Text line (String)",
+        "title": "",
+        "type": "string"
+      },
+      "title": "Mots cl\u00e9s",
+      "type": "array",
+      "uniqueItems": true,
+      "widgetOptions": {
+        "vocabulary": {
+          "@id": "https://annuaire.preprod.imio.be/@vocabularies/plone.app.vocabularies.Keywords"
+        }
+      }
+    },
+    "subtitle": {
+      "behavior": "imio.directory.core.contents.contact.content.IContact",
+      "description": "",
+      "factory": "Text line (String)",
+      "title": "Sous-titre",
+      "type": "string"
+    },
+    "taxonomy_contact_category": {
+      "additionalItems": true,
+      "behavior": "collective.taxonomy.generated.contact_category",
+      "description": "Important! Ces cat\u00e9gories sont utilis\u00e9es pour am\u00e9liorer la recherche et filtrer les fiches dans l'annuaire",
+      "factory": "List",
+      "items": {
+        "description": "",
+        "factory": "Choice",
+        "title": "",
+        "type": "string",
+        "vocabulary": {
+          "@id": "https://annuaire.preprod.imio.be/@vocabularies/collective.taxonomy.contact_category"
+        }
+      },
+      "title": "Cat\u00e9gorie",
+      "type": "array",
+      "uniqueItems": true
+    },
+    "title": {
+      "behavior": "plone.basic",
+      "description": "",
+      "factory": "Text line (String)",
+      "title": "Titre",
+      "type": "string"
+    },
+    "topics": {
+      "additionalItems": true,
+      "behavior": "imio.smartweb.topics",
+      "description": "Important! Les th\u00e9matiques sont utilis\u00e9es pour filtrer les r\u00e9sultats de la recherche et cr\u00e9er des listes",
+      "factory": "List",
+      "items": {
+        "description": "",
+        "factory": "Choice",
+        "title": "",
+        "type": "string",
+        "vocabulary": {
+          "@id": "https://annuaire.preprod.imio.be/@vocabularies/imio.smartweb.vocabulary.Topics"
+        }
+      },
+      "title": "Th\u00e9matiques",
+      "type": "array",
+      "uniqueItems": true
+    },
+    "type": {
+      "behavior": "imio.directory.core.contents.contact.content.IContact",
+      "description": "",
+      "factory": "Choice",
+      "title": "Type",
+      "type": "string",
+      "vocabulary": {
+        "@id": "https://annuaire.preprod.imio.be/@vocabularies/imio.directory.vocabulary.ContactTypes"
+      }
+    },
+    "urls": {
+      "additionalItems": true,
+      "behavior": "imio.directory.core.contents.contact.content.IContact",
+      "description": "",
+      "factory": "List",
+      "items": {
+        "description": "",
+        "factory": "File",
+        "properties": {
+          "type": {
+            "description": "",
+            "factory": "Choice",
+            "title": "Type",
+            "type": "string",
+            "vocabulary": {
+              "@id": "https://annuaire.preprod.imio.be/@vocabularies/imio.directory.vocabulary.SiteTypes"
+            }
+          },
+          "url": {
+            "description": "",
+            "factory": "URL",
+            "title": "URL",
+            "type": "string",
+            "widget": "url"
+          }
+        },
+        "title": "Value",
+        "type": "object"
+      },
+      "title": "URLs",
+      "type": "array",
+      "uniqueItems": false
+    },
+    "vat_number": {
+      "behavior": "imio.directory.core.contents.contact.content.IContact",
+      "description": "",
+      "factory": "Text line (String)",
+      "title": "Num\u00e9ro d'entreprise / TVA",
+      "type": "string"
+    },
+    "zipcode": {
+      "behavior": "imio.directory.core.contents.contact.content.IContact",
+      "description": "",
+      "factory": "Integer",
+      "title": "Code postal",
+      "type": "integer"
+    }
+  },
+  "required": [
+    "title",
+    "type"
+  ],
+  "title": "Contact",
+  "type": "object"
+}
diff --git a/tests/test_plone_restapi.py b/tests/test_plone_restapi.py
index 4f8cfc19..2253dc67 100644
--- a/tests/test_plone_restapi.py
+++ b/tests/test_plone_restapi.py
@@ -291,16 +291,33 @@ def test_get_content_types(app, connector, token):
     with utils.mock_url(url=url, response=json_get_data('get_content_types')):
         resp = app.get(endpoint)
     assert not resp.json['err']
     assert len(resp.json['data']) == 10
     assert resp.json['data'][2]['id'] == 'imio.directory.Contact'
     assert resp.json['data'][2]['text'] == 'Contact'
 
 
+def test_get_content_type(app, connector, token):
+    endpoint = utils.generic_endpoint_url('plone-restapi', 'get_content_type', slug=connector.slug)
+    assert endpoint == '/plone-restapi/my_connector/get_content_type'
+    url = connector.service_url + '/@types/imio.directory.Contact'
+    params = {'id': 'imio.directory.Contact'}
+    with utils.mock_url(url=url, response=json_get_data('get_content_type')):
+        resp = app.get(endpoint, params=params)
+    assert not resp.json['err']
+    assert resp.json['data']['title'] == 'Contact'
+    assert resp.json['data']['required'] == ['title', 'type']
+    assert len(resp.json['data']['properties']) == 28
+    assert (
+        resp.json['data']['properties']['topics']['items']['vocabulary']['@id']
+        == 'https://annuaire.preprod.imio.be/@vocabularies/imio.smartweb.vocabulary.Topics'
+    )
+
+
 def test_create(app, connector, token):
     endpoint = utils.generic_endpoint_url('plone-restapi', 'create', slug=connector.slug)
     assert endpoint == '/plone-restapi/my_connector/create'
     url = connector.service_url + '/braine-l-alleud'
     payload = {
         '@type': 'imio.directory.Contact',
         'title': "Test Entr'ouvert",
         'type': 'organization',
-- 
2.30.2

