Browse Source

graphql enabled and configured with existing content architecture

bach 1 year ago
parent
commit
787494bfc8
23 changed files with 1403 additions and 3 deletions
  1. 5 0
      src/composer.json
  2. 183 1
      src/composer.lock
  3. 42 1
      src/config/sync/core.entity_form_display.node.entite.default.yml
  4. 16 0
      src/config/sync/core.entity_form_display.paragraph.entite_concernement.default.yml
  5. 14 0
      src/config/sync/core.entity_view_display.node.entite.default.yml
  6. 2 0
      src/config/sync/core.entity_view_display.node.entite.teaser.yml
  7. 24 0
      src/config/sync/core.entity_view_display.paragraph.entite_concernement.default.yml
  8. 3 0
      src/config/sync/core.extension.yml
  9. 1 1
      src/config/sync/field.field.node.concernement.field_entite.yml
  10. 23 0
      src/config/sync/field.field.node.entite.field_liens.yml
  11. 23 0
      src/config/sync/field.field.paragraph.entite_concernement.field_angle.yml
  12. 23 0
      src/config/sync/field.field.paragraph.entite_concernement.field_rayon.yml
  13. 19 0
      src/config/sync/field.storage.node.field_liens.yml
  14. 20 0
      src/config/sync/field.storage.paragraph.field_angle.yml
  15. 20 0
      src/config/sync/field.storage.paragraph.field_rayon.yml
  16. 19 0
      src/config/sync/graphql.graphql_servers.ouatterrir.yml
  17. 1 0
      src/config/sync/language.types.yml
  18. 98 0
      src/patches/graphql-fix-invalid-translation-language-dev.patch
  19. 105 0
      src/web/modules/custom/ouatt_graphql/graphql/ouatt_extension.base.graphqls
  20. 48 0
      src/web/modules/custom/ouatt_graphql/graphql/ouatt_extension.extension.graphqls
  21. 9 0
      src/web/modules/custom/ouatt_graphql/ouatt_graphql.info.yml
  22. 47 0
      src/web/modules/custom/ouatt_graphql/src/Plugin/GraphQL/Schema/OuattSchema.php
  23. 658 0
      src/web/modules/custom/ouatt_graphql/src/Plugin/GraphQL/SchemaExtension/OuattSchemaExtension.php

+ 5 - 0
src/composer.json

@@ -20,8 +20,10 @@
         "drupal/core-composer-scaffold": "^9.4",
         "drupal/core-project-message": "^9.4",
         "drupal/core-recommended": "^9.4",
+        "drupal/graphql": "4.x-dev@dev",
         "drupal/paragraphs": "^1.15",
         "drupal/structure_sync": "^2.0",
+        "drupal/typed_data": "1.x-dev@dev",
         "wikimedia/composer-merge-plugin": "^2.0"
     },
     "conflict": {
@@ -108,6 +110,9 @@
         "patches": {
             "drupal/paragraphs": {
                 "Skip saving empty paragraphs for certain types https://www.drupal.org/project/paragraphs/issues/2877695" : "https://www.drupal.org/files/issues/2021-03-31/2877695-35.patch"
+            },
+            "drupal/graphql": {
+              "invalid translation language https://github.com/drupal-graphql/graphql/pull/1176": "./patches/graphql-fix-invalid-translation-language-dev.patch"
             }
           }
     }

+ 183 - 1
src/composer.lock

@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "9f22f2a18cec2f9fe341a284cef4d4bb",
+    "content-hash": "c28f37daf42fd941e5a8be2f3cb6e2a2",
     "packages": [
         {
             "name": "alchemy/zippy",
@@ -5297,6 +5297,69 @@
                 "issues": "http://drupal.org/project/issues/formatter_suite"
             }
         },
+        {
+            "name": "drupal/graphql",
+            "version": "dev-4.x",
+            "source": {
+                "type": "git",
+                "url": "https://git.drupalcode.org/project/graphql.git",
+                "reference": "dedd45ccdea0430b7820acbad4557dc443eff196"
+            },
+            "require": {
+                "drupal/core": "^9.3 || ^10",
+                "drupal/typed_data": "*",
+                "php": ">=7.3",
+                "webonyx/graphql-php": "^14.8.0"
+            },
+            "require-dev": {
+                "drupal/node-node": "*"
+            },
+            "type": "drupal-module",
+            "extra": {
+                "branch-alias": {
+                    "dev-4.x": "4.x-dev"
+                },
+                "drupal": {
+                    "version": "8.x-4.3+11-dev",
+                    "datestamp": "1662992472",
+                    "security-coverage": {
+                        "status": "not-covered",
+                        "message": "Dev releases are not covered by Drupal security advisories."
+                    }
+                }
+            },
+            "notification-url": "https://packages.drupal.org/8/downloads",
+            "license": [
+                "GPL-2.0+"
+            ],
+            "authors": [
+                {
+                    "name": "fubhy",
+                    "homepage": "https://www.drupal.org/user/761344"
+                },
+                {
+                    "name": "hideaway",
+                    "homepage": "https://www.drupal.org/user/741876"
+                },
+                {
+                    "name": "joaogarin",
+                    "homepage": "https://www.drupal.org/user/612814"
+                },
+                {
+                    "name": "klausi",
+                    "homepage": "https://www.drupal.org/user/262198"
+                },
+                {
+                    "name": "pmelab",
+                    "homepage": "https://www.drupal.org/user/555322"
+                }
+            ],
+            "description": "Exposes your Drupal data model through a GraphQL schema.",
+            "homepage": "http://drupal.org/project/graphql",
+            "support": {
+                "source": "https://git.drupalcode.org/project/graphql"
+            }
+        },
         {
             "name": "drupal/honeypot",
             "version": "2.1.1",
@@ -7592,6 +7655,57 @@
                 "source": "https://git.drupalcode.org/project/translation_views"
             }
         },
+        {
+            "name": "drupal/typed_data",
+            "version": "dev-1.x",
+            "source": {
+                "type": "git",
+                "url": "https://git.drupalcode.org/project/typed_data.git",
+                "reference": "94141cf338e5a3a3ec8bdea276bce6a0a84c4bfa"
+            },
+            "require": {
+                "drupal/core": "^9.1 || ^10"
+            },
+            "type": "drupal-module",
+            "extra": {
+                "branch-alias": {
+                    "dev-1.x": "1.x-dev"
+                },
+                "drupal": {
+                    "version": "8.x-1.0-beta1+12-dev",
+                    "datestamp": "1650833384",
+                    "security-coverage": {
+                        "status": "not-covered",
+                        "message": "Dev releases are not covered by Drupal security advisories."
+                    }
+                },
+                "drush": {
+                    "services": {
+                        "drush.services.yml": "^9 || ^10"
+                    }
+                }
+            },
+            "notification-url": "https://packages.drupal.org/8/downloads",
+            "license": [
+                "GPL-2.0-or-later"
+            ],
+            "authors": [
+                {
+                    "name": "TR",
+                    "homepage": "https://www.drupal.org/user/202830"
+                },
+                {
+                    "name": "fago",
+                    "homepage": "https://www.drupal.org/user/16747"
+                }
+            ],
+            "description": "Extends the core Typed Data API with new APIs and features.",
+            "homepage": "https://www.drupal.org/project/typed_data",
+            "support": {
+                "source": "https://git.drupalcode.org/project/typed_data",
+                "issues": "https://www.drupal.org/project/issues/typed_data"
+            }
+        },
         {
             "name": "drupal/ultimate_cron",
             "version": "2.0.0-alpha5",
@@ -13372,6 +13486,72 @@
             "abandoned": "symfony/filesystem",
             "time": "2015-12-17T08:42:14+00:00"
         },
+        {
+            "name": "webonyx/graphql-php",
+            "version": "v14.11.6",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/webonyx/graphql-php.git",
+                "reference": "6070542725b61fc7d0654a8a9855303e5e157434"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/webonyx/graphql-php/zipball/6070542725b61fc7d0654a8a9855303e5e157434",
+                "reference": "6070542725b61fc7d0654a8a9855303e5e157434",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "ext-mbstring": "*",
+                "php": "^7.1 || ^8"
+            },
+            "require-dev": {
+                "amphp/amp": "^2.3",
+                "doctrine/coding-standard": "^6.0",
+                "nyholm/psr7": "^1.2",
+                "phpbench/phpbench": "^1.2",
+                "phpstan/extension-installer": "^1.0",
+                "phpstan/phpstan": "0.12.82",
+                "phpstan/phpstan-phpunit": "0.12.18",
+                "phpstan/phpstan-strict-rules": "0.12.9",
+                "phpunit/phpunit": "^7.2 || ^8.5",
+                "psr/http-message": "^1.0",
+                "react/promise": "2.*",
+                "simpod/php-coveralls-mirror": "^3.0",
+                "squizlabs/php_codesniffer": "3.5.4"
+            },
+            "suggest": {
+                "psr/http-message": "To use standard GraphQL server",
+                "react/promise": "To leverage async resolving on React PHP platform"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "GraphQL\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "A PHP port of GraphQL reference implementation",
+            "homepage": "https://github.com/webonyx/graphql-php",
+            "keywords": [
+                "api",
+                "graphql"
+            ],
+            "support": {
+                "issues": "https://github.com/webonyx/graphql-php/issues",
+                "source": "https://github.com/webonyx/graphql-php/tree/v14.11.6"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/webonyx-graphql-php",
+                    "type": "open_collective"
+                }
+            ],
+            "time": "2022-04-13T16:25:32+00:00"
+        },
         {
             "name": "wikimedia/composer-merge-plugin",
             "version": "v2.0.1",
@@ -13430,6 +13610,8 @@
     "aliases": [],
     "minimum-stability": "stable",
     "stability-flags": {
+        "drupal/graphql": 20,
+        "drupal/typed_data": 20,
         "drupal/autocomplete_deluxe": 5,
         "drupal/better_exposed_filters": 10,
         "drupal/bulkdelete": 20,

+ 42 - 1
src/config/sync/core.entity_form_display.node.entite.default.yml

@@ -6,6 +6,7 @@ dependencies:
     - field.field.node.entite.body
     - field.field.node.entite.field_documents
     - field.field.node.entite.field_images
+    - field.field.node.entite.field_liens
     - field.field.node.entite.field_videos
     - image.style.thumbnail
     - node.type.entite
@@ -13,6 +14,7 @@ dependencies:
     - field_group
     - file
     - image
+    - link
     - text
     - video_embed_field
 third_party_settings:
@@ -21,6 +23,8 @@ third_party_settings:
       children:
         - group_texte
         - group_media
+        - group_documents
+        - group_liens
       label: Tabs
       region: content
       parent_name: ''
@@ -36,7 +40,6 @@ third_party_settings:
       children:
         - field_images
         - field_videos
-        - field_documents
       label: Media
       region: content
       parent_name: group_tabs
@@ -83,6 +86,36 @@ third_party_settings:
         description: ''
         required_fields: true
         weight: -100
+    group_documents:
+      children:
+        - field_documents
+      label: Documents
+      region: content
+      parent_name: group_tabs
+      weight: 17
+      format_type: tab
+      format_settings:
+        classes: ''
+        show_empty_fields: false
+        id: ''
+        formatter: closed
+        description: ''
+        required_fields: true
+    group_liens:
+      children:
+        - field_liens
+      label: Liens
+      region: content
+      parent_name: group_tabs
+      weight: 18
+      format_type: tab
+      format_settings:
+        classes: ''
+        show_empty_fields: false
+        id: ''
+        formatter: closed
+        description: ''
+        required_fields: true
 id: node.entite.default
 targetEntityType: node
 bundle: entite
@@ -119,6 +152,14 @@ content:
       progress_indicator: throbber
       preview_image_style: thumbnail
     third_party_settings: {  }
+  field_liens:
+    type: link_default
+    weight: 26
+    region: content
+    settings:
+      placeholder_url: ''
+      placeholder_title: ''
+    third_party_settings: {  }
   field_videos:
     type: video_embed_field_textfield
     weight: 12

+ 16 - 0
src/config/sync/core.entity_form_display.paragraph.entite_concernement.default.yml

@@ -3,13 +3,22 @@ langcode: fr
 status: true
 dependencies:
   config:
+    - field.field.paragraph.entite_concernement.field_angle
     - field.field.paragraph.entite_concernement.field_entite
+    - field.field.paragraph.entite_concernement.field_rayon
     - paragraphs.paragraphs_type.entite_concernement
 id: paragraph.entite_concernement.default
 targetEntityType: paragraph
 bundle: entite_concernement
 mode: default
 content:
+  field_angle:
+    type: number
+    weight: 2
+    region: content
+    settings:
+      placeholder: ''
+    third_party_settings: {  }
   field_entite:
     type: entity_reference_autocomplete
     weight: 0
@@ -20,6 +29,13 @@ content:
       size: 60
       placeholder: ''
     third_party_settings: {  }
+  field_rayon:
+    type: number
+    weight: 1
+    region: content
+    settings:
+      placeholder: ''
+    third_party_settings: {  }
 hidden:
   created: true
   status: true

+ 14 - 0
src/config/sync/core.entity_view_display.node.entite.default.yml

@@ -6,11 +6,13 @@ dependencies:
     - field.field.node.entite.body
     - field.field.node.entite.field_documents
     - field.field.node.entite.field_images
+    - field.field.node.entite.field_liens
     - field.field.node.entite.field_videos
     - node.type.entite
   module:
     - file
     - image
+    - link
     - text
     - user
     - video_embed_field
@@ -45,6 +47,18 @@ content:
     third_party_settings: {  }
     weight: 102
     region: content
+  field_liens:
+    type: link
+    label: above
+    settings:
+      trim_length: 80
+      url_only: false
+      url_plain: false
+      rel: ''
+      target: ''
+    third_party_settings: {  }
+    weight: 105
+    region: content
   field_videos:
     type: video_embed_field_video
     label: above

+ 2 - 0
src/config/sync/core.entity_view_display.node.entite.teaser.yml

@@ -7,6 +7,7 @@ dependencies:
     - field.field.node.entite.body
     - field.field.node.entite.field_documents
     - field.field.node.entite.field_images
+    - field.field.node.entite.field_liens
     - field.field.node.entite.field_videos
     - node.type.entite
   module:
@@ -33,6 +34,7 @@ content:
 hidden:
   field_documents: true
   field_images: true
+  field_liens: true
   field_videos: true
   langcode: true
   search_api_excerpt: true

+ 24 - 0
src/config/sync/core.entity_view_display.paragraph.entite_concernement.default.yml

@@ -3,13 +3,26 @@ langcode: fr
 status: true
 dependencies:
   config:
+    - field.field.paragraph.entite_concernement.field_angle
     - field.field.paragraph.entite_concernement.field_entite
+    - field.field.paragraph.entite_concernement.field_rayon
     - paragraphs.paragraphs_type.entite_concernement
 id: paragraph.entite_concernement.default
 targetEntityType: paragraph
 bundle: entite_concernement
 mode: default
 content:
+  field_angle:
+    type: number_decimal
+    label: above
+    settings:
+      thousand_separator: ''
+      decimal_separator: .
+      scale: 2
+      prefix_suffix: true
+    third_party_settings: {  }
+    weight: 2
+    region: content
   field_entite:
     type: entity_reference_label
     label: above
@@ -18,5 +31,16 @@ content:
     third_party_settings: {  }
     weight: 0
     region: content
+  field_rayon:
+    type: number_decimal
+    label: above
+    settings:
+      thousand_separator: ''
+      decimal_separator: .
+      scale: 2
+      prefix_suffix: true
+    third_party_settings: {  }
+    weight: 1
+    region: content
 hidden:
   search_api_excerpt: true

+ 3 - 0
src/config/sync/core.extension.yml

@@ -53,6 +53,7 @@ module:
   filter: 0
   filter_perms: 0
   formatter_suite: 0
+  graphql: 0
   help: 0
   honeypot: 0
   image: 0
@@ -77,6 +78,7 @@ module:
   mysql: 0
   node: 0
   options: 0
+  ouatt_graphql: 0
   page_cache: 0
   pagerer: 0
   path: 0
@@ -99,6 +101,7 @@ module:
   token: 0
   toolbar: 0
   translation_views: 0
+  typed_data: 0
   update: 0
   url_to_video_filter: 0
   user: 0

+ 1 - 1
src/config/sync/field.field.node.concernement.field_entite.yml

@@ -12,7 +12,7 @@ id: node.concernement.field_entite
 field_name: field_entite
 entity_type: node
 bundle: concernement
-label: Entité
+label: Entités
 description: ''
 required: false
 translatable: false

+ 23 - 0
src/config/sync/field.field.node.entite.field_liens.yml

@@ -0,0 +1,23 @@
+uuid: bfb6a38e-4497-490d-8bb0-ce895c2e95b5
+langcode: fr
+status: true
+dependencies:
+  config:
+    - field.storage.node.field_liens
+    - node.type.entite
+  module:
+    - link
+id: node.entite.field_liens
+field_name: field_liens
+entity_type: node
+bundle: entite
+label: Liens
+description: ''
+required: false
+translatable: false
+default_value: {  }
+default_value_callback: ''
+settings:
+  title: 1
+  link_type: 16
+field_type: link

+ 23 - 0
src/config/sync/field.field.paragraph.entite_concernement.field_angle.yml

@@ -0,0 +1,23 @@
+uuid: 3d803a88-cde3-43d3-a266-712210d37188
+langcode: fr
+status: true
+dependencies:
+  config:
+    - field.storage.paragraph.field_angle
+    - paragraphs.paragraphs_type.entite_concernement
+id: paragraph.entite_concernement.field_angle
+field_name: field_angle
+entity_type: paragraph
+bundle: entite_concernement
+label: Angle
+description: ''
+required: false
+translatable: false
+default_value: {  }
+default_value_callback: ''
+settings:
+  min: !!float 0
+  max: !!float 360
+  prefix: ''
+  suffix: ''
+field_type: decimal

+ 23 - 0
src/config/sync/field.field.paragraph.entite_concernement.field_rayon.yml

@@ -0,0 +1,23 @@
+uuid: 108ef2b1-7685-4ffc-a9b3-a6b0d7c88101
+langcode: fr
+status: true
+dependencies:
+  config:
+    - field.storage.paragraph.field_rayon
+    - paragraphs.paragraphs_type.entite_concernement
+id: paragraph.entite_concernement.field_rayon
+field_name: field_rayon
+entity_type: paragraph
+bundle: entite_concernement
+label: rayon
+description: ''
+required: false
+translatable: false
+default_value: {  }
+default_value_callback: ''
+settings:
+  min: !!float 0
+  max: !!float 100
+  prefix: ''
+  suffix: ''
+field_type: decimal

+ 19 - 0
src/config/sync/field.storage.node.field_liens.yml

@@ -0,0 +1,19 @@
+uuid: 9c3df3fe-a34f-41a6-a9d3-d12c116ca711
+langcode: fr
+status: true
+dependencies:
+  module:
+    - link
+    - node
+id: node.field_liens
+field_name: field_liens
+entity_type: node
+type: link
+settings: {  }
+module: link
+locked: false
+cardinality: -1
+translatable: true
+indexes: {  }
+persist_with_no_fields: false
+custom_storage: false

+ 20 - 0
src/config/sync/field.storage.paragraph.field_angle.yml

@@ -0,0 +1,20 @@
+uuid: 745fa859-a99d-4ebf-94d5-eaad168db650
+langcode: fr
+status: true
+dependencies:
+  module:
+    - paragraphs
+id: paragraph.field_angle
+field_name: field_angle
+entity_type: paragraph
+type: decimal
+settings:
+  precision: 10
+  scale: 2
+module: core
+locked: false
+cardinality: 1
+translatable: true
+indexes: {  }
+persist_with_no_fields: false
+custom_storage: false

+ 20 - 0
src/config/sync/field.storage.paragraph.field_rayon.yml

@@ -0,0 +1,20 @@
+uuid: 0dcd010f-36ff-4d18-b29e-7a3615d1fd4f
+langcode: fr
+status: true
+dependencies:
+  module:
+    - paragraphs
+id: paragraph.field_rayon
+field_name: field_rayon
+entity_type: paragraph
+type: decimal
+settings:
+  precision: 10
+  scale: 2
+module: core
+locked: false
+cardinality: 1
+translatable: true
+indexes: {  }
+persist_with_no_fields: false
+custom_storage: false

+ 19 - 0
src/config/sync/graphql.graphql_servers.ouatterrir.yml

@@ -0,0 +1,19 @@
+uuid: b3116921-3275-4e06-ba9d-e2d186fda048
+langcode: fr
+status: true
+dependencies: {  }
+name: ouatterrir
+label: ouatterrir
+endpoint: /gql
+debug_flag: 1
+schema: composable
+caching: true
+batching: true
+disable_introspection: false
+query_depth: null
+query_complexity: null
+schema_configuration:
+  composable:
+    extensions:
+      ouatt_extension: ouatt_extension
+persisted_queries_settings: {  }

+ 1 - 0
src/config/sync/language.types.yml

@@ -17,4 +17,5 @@ negotiation:
       language-url-fallback: 1
   language_interface:
     enabled:
+      language-graphql-operation: -999
       language-url: 0

+ 98 - 0
src/patches/graphql-fix-invalid-translation-language-dev.patch

@@ -0,0 +1,98 @@
+diff --git a/src/Plugin/GraphQL/DataProducer/Entity/EntityLoad.php b/src/Plugin/GraphQL/DataProducer/Entity/EntityLoad.php
+index 867a28c..386b2b3 100644
+--- a/src/Plugin/GraphQL/DataProducer/Entity/EntityLoad.php
++++ b/src/Plugin/GraphQL/DataProducer/Entity/EntityLoad.php
+@@ -172,8 +172,10 @@ class EntityLoad extends DataProducerPluginBase implements ContainerFactoryPlugi
+ 
+       // Get the correct translation.
+       if (isset($language) && $language !== $entity->language()->getId() && $entity instanceof TranslatableInterface) {
+-        $entity = $entity->getTranslation($language);
+-        $entity->addCacheContexts(["static:language:{$language}"]);
++        if ($entity->hasTranslation($language)) {
++          $entity = $entity->getTranslation($language);
++          $entity->addCacheContexts(["static:language:{$language}"]);
++        }
+       }
+ 
+       // Check if the passed user (or current user if none is passed) has access
+diff --git a/src/Plugin/GraphQL/DataProducer/Entity/EntityLoadByUuid.php b/src/Plugin/GraphQL/DataProducer/Entity/EntityLoadByUuid.php
+index 10e2d40..e4e6ed0 100644
+--- a/src/Plugin/GraphQL/DataProducer/Entity/EntityLoadByUuid.php
++++ b/src/Plugin/GraphQL/DataProducer/Entity/EntityLoadByUuid.php
+@@ -165,8 +165,10 @@ class EntityLoadByUuid extends DataProducerPluginBase implements ContainerFactor
+ 
+       // Get the correct translation.
+       if (isset($language) && $language != $entity->language()->getId() && $entity instanceof TranslatableInterface) {
+-        $entity = $entity->getTranslation($language);
+-        $entity->addCacheContexts(["static:language:{$language}"]);
++        if ($entity->hasTranslation($language)) {
++          $entity = $entity->getTranslation($language);
++          $entity->addCacheContexts(["static:language:{$language}"]);
++        }
+       }
+ 
+       // Check if the passed user (or current user if none is passed) has access
+diff --git a/src/Plugin/GraphQL/DataProducer/Entity/EntityLoadMultiple.php b/src/Plugin/GraphQL/DataProducer/Entity/EntityLoadMultiple.php
+index 2a0259c..27a19e5 100644
+--- a/src/Plugin/GraphQL/DataProducer/Entity/EntityLoadMultiple.php
++++ b/src/Plugin/GraphQL/DataProducer/Entity/EntityLoadMultiple.php
+@@ -172,8 +172,10 @@ class EntityLoadMultiple extends DataProducerPluginBase implements ContainerFact
+         }
+ 
+         if (isset($language) && $language !== $entities[$id]->language()->getId() && $entities[$id] instanceof TranslatableInterface) {
+-          $entities[$id] = $entities[$id]->getTranslation($language);
+-          $entities[$id]->addCacheContexts(["static:language:{$language}"]);
++          if ($entities[$id]->hasTranslation($language)) {
++            $entities[$id] = $entities[$id]->getTranslation($language);
++            $entities[$id]->addCacheContexts(["static:language:{$language}"]);
++          }
+         }
+ 
+         if ($access) {
+diff --git a/src/Plugin/GraphQL/DataProducer/Entity/EntityTranslation.php b/src/Plugin/GraphQL/DataProducer/Entity/EntityTranslation.php
+index 633bdc2..3773a9b 100644
+--- a/src/Plugin/GraphQL/DataProducer/Entity/EntityTranslation.php
++++ b/src/Plugin/GraphQL/DataProducer/Entity/EntityTranslation.php
+@@ -101,7 +101,7 @@ class EntityTranslation extends DataProducerPluginBase implements ContainerFacto
+    * @return \Drupal\Core\Entity\EntityInterface|null
+    */
+   public function resolve(EntityInterface $entity, $language, ?bool $access, ?AccountInterface $accessUser, ?string $accessOperation) {
+-    if ($entity instanceof TranslatableInterface && $entity->isTranslatable()) {
++    if ($entity instanceof TranslatableInterface && $entity->isTranslatable() && $entity->hasTranslation($language)) {
+       $entity = $entity->getTranslation($language);
+       $entity->addCacheContexts(["static:language:{$language}"]);
+       // Check if the passed user (or current user if none is passed) has access
+diff --git a/src/Plugin/GraphQL/DataProducer/Routing/RouteEntity.php b/src/Plugin/GraphQL/DataProducer/Routing/RouteEntity.php
+index 10ea4e6..1eef22a 100644
+--- a/src/Plugin/GraphQL/DataProducer/Routing/RouteEntity.php
++++ b/src/Plugin/GraphQL/DataProducer/Routing/RouteEntity.php
+@@ -126,8 +126,10 @@ class RouteEntity extends DataProducerPluginBase implements ContainerFactoryPlug
+ 
+         // Get the correct translation.
+         if (isset($language) && $language != $entity->language()->getId() && $entity instanceof TranslatableInterface) {
+-          $entity = $entity->getTranslation($language);
+-          $entity->addCacheContexts(["static:language:{$language}"]);
++          if ($entity->hasTranslation($language)) {
++            $entity = $entity->getTranslation($language);
++            $entity->addCacheContexts(["static:language:{$language}"]);
++          }
+         }
+ 
+         $access = $entity->access('view', NULL, TRUE);
+diff --git a/src/Plugin/GraphQL/DataProducer/Taxonomy/TaxonomyLoadTree.php b/src/Plugin/GraphQL/DataProducer/Taxonomy/TaxonomyLoadTree.php
+index 1bcd624..8b2caf2 100644
+--- a/src/Plugin/GraphQL/DataProducer/Taxonomy/TaxonomyLoadTree.php
++++ b/src/Plugin/GraphQL/DataProducer/Taxonomy/TaxonomyLoadTree.php
+@@ -175,8 +175,10 @@ class TaxonomyLoadTree extends DataProducerPluginBase implements ContainerFactor
+         $context->addCacheableDependency($entities[$id]);
+ 
+         if (isset($language) && $language !== $entities[$id]->language()->getId() && $entities[$id] instanceof TranslatableInterface) {
+-          $entities[$id] = $entities[$id]->getTranslation($language);
+-          $entities[$id]->addCacheContexts(["static:language:{$language}"]);
++          if ($entities[$id]->hasTranslation($language)) {
++            $entities[$id] = $entities[$id]->getTranslation($language);
++            $entities[$id]->addCacheContexts(["static:language:{$language}"]);
++          }
+         }
+ 
+         if ($access) {

+ 105 - 0
src/web/modules/custom/ouatt_graphql/graphql/ouatt_extension.base.graphqls

@@ -0,0 +1,105 @@
+scalar Violation
+
+interface NodeInterface {
+  id: Int!
+  path: String!
+}
+
+type Concernement implements NodeInterface {
+  id: Int!
+  uuid: String!
+  bundle: String!
+  title: String!
+  path: String!
+  author: String
+  texte: String
+  entites: [Entiteintegre]
+}
+
+type Entiteintegre {
+  entite: Entite
+  rayon: Float
+  angle: Float
+}
+
+type Entite implements NodeInterface {
+  id: Int!
+  uuid: String!
+  bundle: String!
+  title: String!
+  path: String!
+  author: String
+  texte: String
+  images: [Image]
+  fichiers: [File]
+  liens: [Link]
+}
+
+type Static implements NodeInterface {
+  id: Int!
+  uuid: String!
+  bundle: String!
+  title: String!
+  path: String!
+  author: String
+  texte: String
+}
+
+type Filefield {
+  file: File!
+  description: String
+}
+
+type File {
+  fid: String!
+  uuid: String!
+  filename: String!
+  filemime: String!
+  filesize: String!
+  url: String!
+}
+
+type Taxoterm {
+  id: Int!
+  uuid: String!
+  name: String!
+  vocabulary: String
+}
+
+type VideoLink {
+  url: String
+}
+
+type Image {
+  id: Int!
+  url: String!
+  alt: String
+  # style_minicard: ImageStyle
+  # style_minicard_url: String
+  # style_linkedmaterialcard: ImageStyle
+  # style_linkedmaterialcard_url: String
+  # style_cardmedium: ImageStyle
+  # style_cardmedium_url: String
+  # style_cardfull: ImageStyle
+  # style_cardfull_url: String
+  # style_articlecardmedium: ImageStyle
+  # style_articlecardmedium_url: String
+  # style_hd: ImageStyle
+  # style_hd_url: String
+}
+
+type ImageStyle {
+  width: Int
+  height: Int
+  url: String
+}
+
+type Link {
+  url: String
+  title: String
+}
+
+type Date {
+  start: String
+  end: String
+}

+ 48 - 0
src/web/modules/custom/ouatt_graphql/graphql/ouatt_extension.extension.graphqls

@@ -0,0 +1,48 @@
+# extend type Query {
+#   route(path: String!): NodeInterface
+# }
+
+
+extend type Query {
+  allconcernements: [Concernement]
+}
+
+extend type Query {
+  concernements(ids: [Int]): [Concernement]
+}
+
+extend type Query {
+  concernement(id: Int!): Concernement
+}
+
+extend type Query {
+  allentites: [Entite]
+}
+
+extend type Query {
+  entites(ids: [Int]): [Entite]
+}
+
+extend type Query {
+  entite(id: Int!): Entite
+}
+
+extend type Query {
+  allstatics: [Static]
+}
+
+extend type Query {
+  statics(ids: [Int]): [Static]
+}
+
+extend type Query {
+  static(id: Int!): Static
+}
+
+# extend type Query {
+#   alltags: [Taxoterm]
+# }
+
+# extend type Query {
+#   tag(id: Int!): Taxoterm
+# }

+ 9 - 0
src/web/modules/custom/ouatt_graphql/ouatt_graphql.info.yml

@@ -0,0 +1,9 @@
+name: Où Atterrir Graphql
+type: module
+description: 'Où Atterrir GraphQL schema.'
+package: Ouatterrir
+core: 8.x
+dependencies:
+  - graphql:graphql
+  - node:node
+core_version_requirement: ^8 || ^9

+ 47 - 0
src/web/modules/custom/ouatt_graphql/src/Plugin/GraphQL/Schema/OuattSchema.php

@@ -0,0 +1,47 @@
+<?php
+
+namespace Drupal\ouatt_graphql\Plugin\GraphQL\Schema;
+
+use Drupal\graphql\Plugin\GraphQL\Schema\ComposableSchema;
+use Drupal\graphql\GraphQL\ResolverBuilder;
+use Drupal\graphql\GraphQL\ResolverRegistry;
+// use Drupal\graphql\Plugin\GraphQL\Schema\SdlSchemaPluginBase;
+use GraphQL\Error\Error;
+/**
+ * @Schema(
+ *   id = "ouatt",
+ *   name = "Ou Atterrir schema",
+ *   extensions = "ouatt",
+ * )
+ */
+class OuattSchema extends ComposableSchema {
+
+  // /**
+  //  * {@inheritdoc}
+  //  */
+  // public function getResolverRegistry() {
+  //   $builder = new ResolverBuilder();
+  //   $registry = new ResolverRegistry();
+  //
+  //   // Tell GraphQL how to resolve types of a common interface.
+  //   $registry->addTypeResolver('NodeInterface', function ($value) {
+  //     if ($value instanceof NodeInterface) {
+  //       switch ($value->bundle()) {
+  //         case 'article': return 'Article';
+  //         case 'materiau': return 'Materiau';
+  //       }
+  //     }
+  //     throw new Error('Could not resolve content type.');
+  //   });
+  //
+  //   $registry->addFieldResolver('Query', 'route', $builder->compose(
+  //     $builder->produce('route_load')
+  //       ->map('path', $builder->fromArgument('path')),
+  //     $builder->produce('route_entity')
+  //       ->map('url', $builder->fromParent())
+  //   ));
+  //
+  //   return $registry;
+  // }
+
+}

+ 658 - 0
src/web/modules/custom/ouatt_graphql/src/Plugin/GraphQL/SchemaExtension/OuattSchemaExtension.php

@@ -0,0 +1,658 @@
+<?php
+
+namespace Drupal\ouatt_graphql\Plugin\GraphQL\SchemaExtension;
+
+use Drupal\graphql\GraphQL\ResolverBuilder;
+use Drupal\graphql\GraphQL\ResolverRegistryInterface;
+use Drupal\graphql\GraphQL\Response\ResponseInterface;
+use Drupal\graphql\Plugin\GraphQL\SchemaExtension\SdlSchemaExtensionPluginBase;
+use Drupal\materio_graphql\GraphQL\Response\MaterioResponse;
+use Drupal\Core\Url;
+
+use GraphQL\Error\Error;
+
+/**
+ * @SchemaExtension(
+ *   id = "ouatt_extension",
+ *   name = "Ou Atterrir extension",
+ *   description = "Ou Atterrir extension.",
+ *   schema = "ouatt"
+ * )
+ */
+class OuattSchemaExtension extends SdlSchemaExtensionPluginBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function registerResolvers(ResolverRegistryInterface $registry) {
+    $builder = new ResolverBuilder();
+
+    $this->addRouteResolver($registry, $builder);
+    // 
+    $this->addConcernements($registry, $builder);
+    //
+    $this->addEntitesIntegres($registry, $builder);
+    // 
+    $this->addEntites($registry, $builder);
+    // 
+    $this->addStatic($registry, $builder);
+    //
+    $this->addFilefield($registry, $builder);
+    //
+    $this->addFile($registry, $builder);
+    //
+    $this->addDate($registry, $builder);
+    //
+    $this->addVideolink($registry, $builder);
+    //
+    $this->addImage($registry, $builder);
+    //
+    // $this->addTaxoterm($registry, $builder);
+    //
+    $this->addLink($registry, $builder);
+
+  }
+
+  //  ___          _
+  // | _ \___ _  _| |_ ___
+  // |   / _ \ || |  _/ -_)
+  // |_|_\___/\_,_|\__\___|
+  protected function addRouteResolver(ResolverRegistryInterface $registry, ResolverBuilder $builder) {
+    // Tell GraphQL how to resolve types of a common interface.
+    $registry->addTypeResolver('NodeInterface', function ($value) {
+
+      $path = explode('\\', get_class($value));
+      $class = array_pop($path);
+
+      if ($class === 'Node') {
+        switch ($value->bundle()) {
+          case 'concernement': return 'Concernement';
+          case 'entite': return 'Entite';
+        }
+      }
+      throw new Error('Could not resolve content type.');
+    });
+
+    $registry->addFieldResolver('Query', 'route',
+      $builder->compose(
+        $builder->produce('route_load')
+          ->map('path', $builder->fromArgument('path')),
+        $builder->produce('route_entity')
+          ->map('url', $builder->fromParent())
+    ));
+  }
+
+  //    ____                                                          _       
+  //   / ___|___  _ __   ___ ___ _ __ _ __   ___ _ __ ___   ___ _ __ | |_ ___ 
+  //  | |   / _ \| '_ \ / __/ _ \ '__| '_ \ / _ \ '_ ` _ \ / _ \ '_ \| __/ __|
+  //  | |__| (_) | | | | (_|  __/ |  | | | |  __/ | | | | |  __/ | | | |_\__ \
+  //   \____\___/|_| |_|\___\___|_|  |_| |_|\___|_| |_| |_|\___|_| |_|\__|___/
+                                                                         
+  protected function addConcernements(ResolverRegistryInterface $registry, ResolverBuilder $builder){
+    $registry->addFieldResolver('Query', 'allconcernements',
+      $builder->compose(
+          $builder->callback(function($parent, $arg){
+            $entity_storage = \Drupal::entityTypeManager()->getStorage('node');
+            $query = $entity_storage->getQuery()
+              ->condition('type', ['concernement'], 'IN')
+              ->accessCheck(TRUE);
+            $results = $query->execute();
+            return $results;
+          }),
+          $builder->produce('entity_load_multiple')
+          ->map('type', $builder->fromValue('node'))
+          ->map('ids', $builder->fromParent())
+        )
+    );
+
+    $registry->addFieldResolver('Query', 'concernements',
+      $builder->compose(
+          $builder->callback(function($parent, $arg){
+            $entity_storage = \Drupal::entityTypeManager()->getStorage('node');
+            $query = $entity_storage->getQuery()
+              ->condition('type', ['concernement'], 'IN')
+              ->accessCheck(TRUE);
+            $results = $query->execute();
+            return $results;
+          }),
+          $builder->produce('entity_load_multiple')
+          ->map('type', $builder->fromValue('node'))
+          ->map('ids', $builder->fromArgument('ids'))
+        )
+    );
+
+    $registry->addFieldResolver('Query', 'concernement',
+      $builder->produce('entity_load')
+        ->map('type', $builder->fromValue('node'))
+        ->map('id', $builder->fromArgument('id'))
+    );
+
+    $registry->addFieldResolver('Concernement', 'id',
+      $builder->produce('entity_id')
+        ->map('entity', $builder->fromParent())
+      );
+
+    $registry->addFieldResolver('Concernement', 'uuid',
+      $builder->produce('entity_uuid')
+        ->map('entity', $builder->fromParent())
+      );
+
+    $registry->addFieldResolver('Concernement', 'path',
+      $builder->compose(
+        $builder->produce('entity_url')
+          ->map('entity', $builder->fromParent()),
+        $builder->produce('url_path')
+          ->map('url', $builder->fromParent())
+      )
+    );
+
+    $registry->addFieldResolver('Concernement', 'title',
+      $builder->compose(
+        $builder->produce('entity_label')
+          ->map('entity', $builder->fromParent())
+      ));
+
+    $registry->addFieldResolver('Concernement', 'bundle',
+      $builder->compose(
+        $builder->produce('entity_bundle')
+          ->map('entity', $builder->fromParent())
+      ));
+
+    $registry->addFieldResolver('Concernement', 'texte',
+      $builder->produce('property_path')
+        ->map('type', $builder->fromValue('entity:node'))
+        ->map('value', $builder->fromParent())
+        ->map('path', $builder->fromValue('body.value'))
+      );
+
+    $registry->addFieldResolver('Concernement', 'author',
+      $builder->compose(
+        $builder->produce('entity_owner')
+          ->map('entity', $builder->fromParent()),
+        $builder->produce('entity_label')
+          ->map('entity', $builder->fromParent())
+      ));
+
+    $registry->addFieldResolver('Concernement', 'entites',
+      $builder->produce('entity_reference_revisions')
+        ->map('entity', $builder->fromParent())
+        ->map('field', $builder->fromValue('field_entite'))
+      );
+  }
+
+  //   _____       _   _ _    __        ___       _    __             __           
+  //  | ____|_ __ | |_(_) |_ /_/  ___  |_ _|_ __ | |_ /_/  __ _ _ __ /_/  ___  ___ 
+  //  |  _| | '_ \| __| | __/ _ \/ __|  | || '_ \| __/ _ \/ _` | '__/ _ \/ _ \/ __|
+  //  | |___| | | | |_| | ||  __/\__ \  | || | | | ||  __/ (_| | | |  __/  __/\__ \
+  //  |_____|_| |_|\__|_|\__\___||___/ |___|_| |_|\__\___|\__, |_|  \___|\___||___/
+  //                                                      |___/                    
+  protected function addEntitesIntegres(ResolverRegistryInterface $registry, ResolverBuilder $builder) {
+
+    $registry->addFieldResolver('Entiteintegre', 'entite',
+      $builder->compose(
+        $builder->produce('entity_reference')
+          ->map('entity', $builder->fromParent())
+          ->map('field', $builder->fromValue('field_entite')),
+        $builder->callback(function ($items) {
+            return $items[0];
+          })
+      ));
+
+    $registry->addFieldResolver('Entiteintegre', 'rayon',
+      $builder->produce('property_path')
+        ->map('type', $builder->fromValue('entity:paragraph'))
+        ->map('value', $builder->fromParent())
+        ->map('path', $builder->fromValue('field_rayon.value'))
+      );
+
+    $registry->addFieldResolver('Entiteintegre', 'angle',
+      $builder->produce('property_path')
+        ->map('type', $builder->fromValue('entity:paragraph'))
+        ->map('value', $builder->fromParent())
+        ->map('path', $builder->fromValue('field_angle.value'))
+      );
+  }
+
+  //   _____       _   _ _            
+  //  | ____|_ __ | |_(_) |_ ___  ___ 
+  //  |  _| | '_ \| __| | __/ _ \/ __|
+  //  | |___| | | | |_| | ||  __/\__ \
+  //  |_____|_| |_|\__|_|\__\___||___/
+                                 
+  protected function addEntites(ResolverRegistryInterface $registry, ResolverBuilder $builder){
+    $registry->addFieldResolver('Query', 'allentites',
+      $builder->compose(
+          $builder->callback(function($parent, $arg){
+            $entity_storage = \Drupal::entityTypeManager()->getStorage('node');
+            $query = $entity_storage->getQuery()
+              ->condition('type', ['entite'], 'IN')
+              ->accessCheck(TRUE);
+            $results = $query->execute();
+            return $results;
+          }),
+          $builder->produce('entity_load_multiple')
+          ->map('type', $builder->fromValue('node'))
+          ->map('ids', $builder->fromParent())
+        )
+    );
+
+    $registry->addFieldResolver('Query', 'entites',
+      $builder->compose(
+          $builder->callback(function($parent, $arg){
+            $entity_storage = \Drupal::entityTypeManager()->getStorage('node');
+            $query = $entity_storage->getQuery()
+              ->condition('type', ['entite'], 'IN')
+              ->accessCheck(TRUE);
+            $results = $query->execute();
+            return $results;
+          }),
+          $builder->produce('entity_load_multiple')
+          ->map('type', $builder->fromValue('node'))
+          ->map('ids', $builder->fromArgument('ids'))
+        )
+    );
+
+    $registry->addFieldResolver('Query', 'entite',
+      $builder->produce('entity_load')
+        ->map('type', $builder->fromValue('node'))
+        ->map('id', $builder->fromArgument('id'))
+    );
+
+    $registry->addFieldResolver('Entite', 'id',
+      $builder->produce('entity_id')
+        ->map('entity', $builder->fromParent())
+      );
+
+    $registry->addFieldResolver('Entite', 'uuid',
+      $builder->produce('entity_uuid')
+        ->map('entity', $builder->fromParent())
+      );
+
+    $registry->addFieldResolver('Entite', 'path',
+      $builder->compose(
+        $builder->produce('entity_url')
+          ->map('entity', $builder->fromParent()),
+        $builder->produce('url_path')
+          ->map('url', $builder->fromParent())
+      )
+    );
+
+    $registry->addFieldResolver('Entite', 'title',
+      $builder->compose(
+        $builder->produce('entity_label')
+          ->map('entity', $builder->fromParent())
+      ));
+
+    $registry->addFieldResolver('Entite', 'bundle',
+      $builder->compose(
+        $builder->produce('entity_bundle')
+          ->map('entity', $builder->fromParent())
+      ));
+
+    $registry->addFieldResolver('Entite', 'texte',
+      $builder->produce('property_path')
+        ->map('type', $builder->fromValue('entity:node'))
+        ->map('value', $builder->fromParent())
+        ->map('path', $builder->fromValue('body.value'))
+      );
+
+    $registry->addFieldResolver('Entite', 'author',
+      $builder->compose(
+        $builder->produce('entity_owner')
+          ->map('entity', $builder->fromParent()),
+        $builder->produce('entity_label')
+          ->map('entity', $builder->fromParent())
+      ));
+
+    $registry->addFieldResolver('Entite', 'images',
+      $builder->produce('entity_reference')
+        ->map('entity', $builder->fromParent())
+        ->map('field', $builder->fromValue('field_images'))
+      );
+
+    $registry->addFieldResolver('Entite', 'videos',
+      $builder->produce('property_path')
+        ->map('type', $builder->fromValue('entity:node'))
+        ->map('value', $builder->fromParent())
+        ->map('path', $builder->fromValue('field_videos'))
+      );
+
+    $registry->addFieldResolver('Entite', 'fichiers',
+      $builder->produce('entity_reference')
+        ->map('entity', $builder->fromParent())
+        ->map('field', $builder->fromValue('field_fichiers'))
+      );
+
+    $registry->addFieldResolver('Entite', 'liens',
+      $builder->produce('property_path')
+        ->map('type', $builder->fromValue('entity:node'))
+        ->map('value', $builder->fromParent())
+        ->map('path', $builder->fromValue('field_liens'))
+      );
+
+  }
+
+  //  ___ _        _   _
+  // / __| |_ __ _| |_(_)__ ___
+  // \__ \  _/ _` |  _| / _(_-<
+  // |___/\__\__,_|\__|_\__/__/
+  protected function addStatic(ResolverRegistryInterface $registry, ResolverBuilder $builder) {
+
+    $registry->addFieldResolver('Query', 'allstatics',
+      $builder->compose(
+        $builder->callback(function($parent, $arg){
+          $entity_storage = \Drupal::entityTypeManager()->getStorage('node');
+          $query = $entity_storage->getQuery()
+            ->condition('type', ['static'], 'IN')
+            ->accessCheck(TRUE);
+          $results = $query->execute();
+          return $results;
+        }),
+        $builder->produce('entity_load_multiple')
+        ->map('type', $builder->fromValue('node'))
+        ->map('ids', $builder->fromParent())
+      )
+    );
+
+    $registry->addFieldResolver('Query', 'statics',
+      $builder->produce('entity_load_multiple')
+        ->map('type', $builder->fromValue('node'))
+        ->map('ids', $builder->fromArgument('ids'))
+    );
+
+    $registry->addFieldResolver('Query', 'static',
+      $builder->produce('entity_load')
+        ->map('type', $builder->fromValue('node'))
+        ->map('bundles', $builder->fromValue(['static']))
+        ->map('id', $builder->fromArgument('id'))
+    );
+
+    $registry->addFieldResolver('Static', 'id',
+      $builder->produce('entity_id')
+        ->map('entity', $builder->fromParent())
+    );
+
+    $registry->addFieldResolver('Static', 'uuid',
+      $builder->produce('entity_uuid')
+        ->map('entity', $builder->fromParent())
+    );
+
+    $registry->addFieldResolver('Static', 'path',
+      $builder->compose(
+        $builder->produce('entity_url')
+          ->map('entity', $builder->fromParent()),
+        $builder->produce('url_path')
+          ->map('url', $builder->fromParent())
+      )
+    );
+
+    $registry->addFieldResolver('Static', 'title',
+      $builder->compose(
+        $builder->produce('entity_label')
+          ->map('entity', $builder->fromParent())
+    ));
+
+    $registry->addFieldResolver('Static', 'bundle',
+      $builder->compose(
+        $builder->produce('entity_bundle')
+          ->map('entity', $builder->fromParent())
+    ));
+
+    $registry->addFieldResolver('Static', 'texte',
+      $builder->produce('property_path')
+        ->map('type', $builder->fromValue('entity:node'))
+        ->map('value', $builder->fromParent())
+        ->map('path', $builder->fromValue('body.value'))
+    );
+
+    $registry->addFieldResolver('Static', 'author',
+      $builder->compose(
+        $builder->produce('entity_owner')
+          ->map('entity', $builder->fromParent()),
+        $builder->produce('entity_label')
+          ->map('entity', $builder->fromParent())
+    ));
+
+    $registry->addFieldResolver('Static', 'fichiers',
+      $builder->produce('entity_reference')
+        ->map('entity', $builder->fromParent())
+        ->map('field', $builder->fromValue('field_fichier'))
+      );
+
+  }
+
+  //  ___       _
+  // |   \ __ _| |_ ___
+  // | |) / _` |  _/ -_)
+  // |___/\__,_|\__\___|
+  protected function addDate(ResolverRegistryInterface $registry, ResolverBuilder $builder) {
+    $registry->addFieldResolver('Date', 'start',
+      $builder->callback(function ($parent, $args) {
+        return isset($parent[0]) ? $parent[0]['value'] : null;
+      })
+    );
+    $registry->addFieldResolver('Date', 'end',
+      $builder->callback(function ($parent, $args) {
+        return isset($parent[0]) ? $parent[0]['end_value'] : null;
+      })
+    );
+  }
+
+  //  ___ _ _      __ _     _    _
+  // | __(_) |___ / _(_)___| |__| |
+  // | _|| | / -_)  _| / -_) / _` |
+  // |_| |_|_\___|_| |_\___|_\__,_|
+  protected function addFilefield(ResolverRegistryInterface $registry, ResolverBuilder $builder) {
+
+    $registry->addFieldResolver('Filefield', 'description',
+      $builder->callback(function ($parent, $args) {
+        return $parent['description'];
+      })
+    );
+
+    $registry->addFieldResolver('Filefield', 'file',
+      $builder->callback(function ($parent, $args) {
+        return \Drupal\file\Entity\File::load($parent['target_id']);
+      })
+    );
+
+  }
+
+  //  ___ _ _
+  // | __(_) |___
+  // | _|| | / -_)
+  // |_| |_|_\___|
+  protected function addFile(ResolverRegistryInterface $registry, ResolverBuilder $builder) {
+    $registry->addFieldResolver('File', 'url',
+      $builder->callback(function ($parent, $args) {
+        return $parent->createFileUrl();
+      })
+    );
+
+    $registry->addFieldResolver('File', 'filesize',
+      $builder->callback(function ($parent, $args) {
+        return $parent->getSize();
+      })
+    );
+
+    $registry->addFieldResolver('File', 'filemime',
+      $builder->callback(function ($parent, $args) {
+        return $parent->getMimeType();
+      })
+    );
+
+    $registry->addFieldResolver('File', 'filename',
+      $builder->callback(function ($parent, $args) {
+        return $parent->getFilename();
+      })
+    );
+
+    $registry->addFieldResolver('File', 'fid',
+      $builder->callback(function ($parent, $args) {
+        return $parent->id();
+      })
+    );
+
+    $registry->addFieldResolver('File', 'uuid',
+      $builder->callback(function ($parent, $args) {
+        return $parent->uuid();
+      })
+    );
+  }
+
+  //  ___
+  // |_ _|_ __  __ _ __ _ ___
+  //  | || '  \/ _` / _` / -_)
+  // |___|_|_|_\__,_\__, \___|
+  //                |___/
+  protected function addImage(ResolverRegistryInterface $registry, ResolverBuilder $builder) {
+    $registry->addFieldResolver('Image', 'id',
+      $builder->produce('entity_id')
+        ->map('entity', $builder->fromParent())
+      );
+
+    $registry->addFieldResolver('Image', 'url',
+      $builder->produce('image_url')
+        ->map('entity', $builder->fromParent())
+      );
+
+    $registry->addFieldResolver('Image', 'alt',
+      $builder->produce('property_path')
+      ->map('type', $builder->fromValue('entity:node'))
+      ->map('value', $builder->fromParent())
+      ->map('path', $builder->fromValue('field_image.alt'))
+      );
+
+    $registry->addFieldResolver('Image', 'style_minicard',
+      $builder->produce('image_derivative')
+        ->map('entity', $builder->fromParent())
+        ->map('style', $builder->fromValue('card_medium_half'))
+      );
+
+    $registry->addFieldResolver('Image', 'style_minicard_url',
+      $builder->compose(
+        $builder->produce('image_derivative')
+          ->map('entity', $builder->fromParent())
+          ->map('style', $builder->fromValue('card_medium_half')),
+        $builder->callback(function($parent, $args){
+            return $parent['url'];
+          })
+      ));
+
+  }
+
+  // __   ___    _         _    _      _
+  // \ \ / (_)__| |___ ___| |  (_)_ _ | |__
+  //  \ V /| / _` / -_) _ \ |__| | ' \| / /
+  //   \_/ |_\__,_\___\___/____|_|_||_|_\_\
+  protected function addVideolink(ResolverRegistryInterface $registry, ResolverBuilder $builder) {
+    $registry->addFieldResolver('VideoLink', 'url',
+      $builder->produce('property_path')
+        ->map('type', $builder->fromValue('field_item:video_embed_field'))
+        ->map('value', $builder->fromParent())
+        ->map('path', $builder->fromValue('value'))
+      );
+  }
+
+  //  _____
+  // |_   _|_ _ __ _
+  //   | |/ _` / _` |
+  //   |_|\__,_\__, |
+  //           |___/
+  // protected function addTaxoterm(ResolverRegistryInterface $registry, ResolverBuilder $builder) {
+  //   $registry->addFieldResolver('Query', 'alltags',
+  //     $builder->compose(
+  //         $builder->callback(function($parent, $arg){
+  //           $entity_storage = \Drupal::entityTypeManager()->getStorage('taxonomy_term');
+  //           $query = $entity_storage->getQuery()
+  //             ->condition('vid', ['tag','tag_prod'], 'IN')
+  //             ->accessCheck(TRUE);
+  //           $results = $query->execute();
+  //           return $results;
+  //         }),
+  //         $builder->produce('entity_load_multiple')
+  //         ->map('type', $builder->fromValue('taxonomy_term'))
+  //         ->map('ids', $builder->fromParent())
+  //       )
+  //   );
+
+  //   $registry->addFieldResolver('Query', 'tag',
+  //    $builder->produce('entity_load')
+  //      ->map('type', $builder->fromValue('taxonomy_term'))
+  //      ->map('bundles', $builder->fromValue(['tag']))
+  //      ->map('id', $builder->fromArgument('id'))
+  //    );
+  //   $registry->addFieldResolver('Query', 'famille',
+  //     $builder->produce('entity_load')
+  //       ->map('type', $builder->fromValue('taxonomy_term'))
+  //       ->map('bundles', $builder->fromValue(['famille']))
+  //       ->map('id', $builder->fromArgument('id'))
+  //   );
+  //   $registry->addFieldResolver('Query', 'tagprod',
+  //    $builder->produce('entity_load')
+  //      ->map('type', $builder->fromValue('taxonomy_term'))
+  //      ->map('bundles', $builder->fromValue(['tag_prod']))
+  //      ->map('id', $builder->fromArgument('id'))
+  //    );
+
+  //   // get all tags for a given familly
+  //   // $registry->addFieldResolver('Query', 'famillytags',
+  //   //   $builder->compose(
+  //   //     $builder->callback(function($parent, $arg){
+  //   //       $entity_storage = \Drupal::entityTypeManager()->getStorage('node');
+  //   //       $query = $entity_storage->getQuery()
+  //   //         ->condition('type', ['texte', 'texte_prod'], 'IN')
+  //   //         ->condition('field_famille', $arg->famillyid)
+  //   //         ->accessCheck(TRUE);
+  //   //       $results = $query->execute();
+  //   //       return $results;
+  //   //     }),
+  //   //     $builder->produce('entity_load_multiple')
+  //   //     ->map('type', $builder->fromValue('node'))
+  //   //     ->map('ids', $builder->fromParent())
+  //   //     //  $builder->produce('entity_load_multiple')
+  //   //     //  ->map('type', $builder->fromValue('taxonomy_term'))
+  //   //     //  ->map('ids', $builder->fromParent())
+  //   //   )
+  //   // );
+
+
+  //   $registry->addFieldResolver('Taxoterm', 'id',
+  //    $builder->produce('entity_id')
+  //      ->map('entity', $builder->fromParent())
+  //    );
+
+  //   $registry->addFieldResolver('Taxoterm', 'uuid',
+  //    $builder->produce('entity_uuid')
+  //      ->map('entity', $builder->fromParent())
+  //    );
+
+  //   $registry->addFieldResolver('Taxoterm', 'name',
+  //     $builder->produce('entity_label')
+  //       ->map('entity', $builder->fromParent())
+  //     );
+
+  //   $registry->addFieldResolver('Taxoterm', 'vocabulary',
+  //     $builder->produce('entity_bundle')
+  //       ->map('entity', $builder->fromParent())
+  //   );
+  // }
+  
+  
+  //  _    _      _
+  // | |  (_)_ _ | |__
+  // | |__| | ' \| / /
+  // |____|_|_||_|_\_\
+  protected function addLink(ResolverRegistryInterface $registry, ResolverBuilder $builder) {
+    $registry->addFieldResolver('Link', 'url',
+      $builder->callback(function ($parent, $args) {
+        return isset($parent[0]) ? $parent[0]['uri'] : null;
+      })
+    );
+    $registry->addFieldResolver('Link', 'title',
+      $builder->callback(function ($parent, $args) {
+        return isset($parent[0]) ? $parent[0]['title'] : null;
+      })
+    );
+  }
+}