@amazeelabs/silverback-gatsby 3.7.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/drupal/silverback_gatsby/.prettierignore +1 -0
  3. package/drupal/silverback_gatsby/CHANGELOG.md +1216 -0
  4. package/drupal/silverback_gatsby/README.md +297 -0
  5. package/drupal/silverback_gatsby/composer.json +15 -0
  6. package/drupal/silverback_gatsby/directives.gql +63 -0
  7. package/drupal/silverback_gatsby/directives.graphql +62 -0
  8. package/drupal/silverback_gatsby/drush.services.yml +9 -0
  9. package/drupal/silverback_gatsby/graphql/entity.directive.graphqls +2 -0
  10. package/drupal/silverback_gatsby/graphql/menu.directive.graphqls +17 -0
  11. package/drupal/silverback_gatsby/graphql/silverback_gatsby.base.graphqls +25 -0
  12. package/drupal/silverback_gatsby/graphql/silverback_gatsby.extension.graphqls +10 -0
  13. package/drupal/silverback_gatsby/graphql/stringTranslation.directive.graphqls +4 -0
  14. package/drupal/silverback_gatsby/graphql/translatableString.directive.graphqls +2 -0
  15. package/drupal/silverback_gatsby/modules/silverback_gatsby_example/directives.gql +6 -0
  16. package/drupal/silverback_gatsby/modules/silverback_gatsby_example/graphql/.graphqlrc.json +4 -0
  17. package/drupal/silverback_gatsby/modules/silverback_gatsby_example/graphql/silverback_gatsby_example.graphqls +39 -0
  18. package/drupal/silverback_gatsby/modules/silverback_gatsby_example/silverback_gatsby_example.info.yml +7 -0
  19. package/drupal/silverback_gatsby/modules/silverback_gatsby_example/silverback_gatsby_example.module +16 -0
  20. package/drupal/silverback_gatsby/modules/silverback_gatsby_example/src/Directives.php +24 -0
  21. package/drupal/silverback_gatsby/silverback_gatsby.info.yml +8 -0
  22. package/drupal/silverback_gatsby/silverback_gatsby.install +180 -0
  23. package/drupal/silverback_gatsby/silverback_gatsby.links.task.yml +6 -0
  24. package/drupal/silverback_gatsby/silverback_gatsby.module +119 -0
  25. package/drupal/silverback_gatsby/silverback_gatsby.permissions.yml +8 -0
  26. package/drupal/silverback_gatsby/silverback_gatsby.post_update.php +15 -0
  27. package/drupal/silverback_gatsby/silverback_gatsby.routing.yml +28 -0
  28. package/drupal/silverback_gatsby/silverback_gatsby.services.yml +50 -0
  29. package/drupal/silverback_gatsby/src/Annotation/GatsbyFeed.php +41 -0
  30. package/drupal/silverback_gatsby/src/Commands/SilverbackGatsbyCommands.php +102 -0
  31. package/drupal/silverback_gatsby/src/Controller/BuildController.php +45 -0
  32. package/drupal/silverback_gatsby/src/Controller/PublisherController.php +34 -0
  33. package/drupal/silverback_gatsby/src/Directives.php +51 -0
  34. package/drupal/silverback_gatsby/src/GatsbyBuildTrigger.php +230 -0
  35. package/drupal/silverback_gatsby/src/GatsbyBuildTriggerInterface.php +52 -0
  36. package/drupal/silverback_gatsby/src/GatsbyUpdate.php +38 -0
  37. package/drupal/silverback_gatsby/src/GatsbyUpdateHandler.php +181 -0
  38. package/drupal/silverback_gatsby/src/GatsbyUpdateTracker.php +98 -0
  39. package/drupal/silverback_gatsby/src/GatsbyUpdateTrackerInterface.php +59 -0
  40. package/drupal/silverback_gatsby/src/GatsbyUpdateTrigger.php +69 -0
  41. package/drupal/silverback_gatsby/src/GatsbyUpdateTriggerInterface.php +22 -0
  42. package/drupal/silverback_gatsby/src/GraphQL/Build.php +235 -0
  43. package/drupal/silverback_gatsby/src/GraphQL/ComposableSchema.php +73 -0
  44. package/drupal/silverback_gatsby/src/LocaleStorageDecorator.php +181 -0
  45. package/drupal/silverback_gatsby/src/MenuTreeStorageDecorator.php +139 -0
  46. package/drupal/silverback_gatsby/src/Plugin/FeedBase.php +121 -0
  47. package/drupal/silverback_gatsby/src/Plugin/FeedInterface.php +132 -0
  48. package/drupal/silverback_gatsby/src/Plugin/FeedPluginManager.php +39 -0
  49. package/drupal/silverback_gatsby/src/Plugin/Gatsby/Feed/EntityFeed.php +252 -0
  50. package/drupal/silverback_gatsby/src/Plugin/Gatsby/Feed/MenuFeed.php +292 -0
  51. package/drupal/silverback_gatsby/src/Plugin/Gatsby/Feed/StringTranslationFeed.php +171 -0
  52. package/drupal/silverback_gatsby/src/Plugin/Gatsby/Feed/TranslatableStringFeed.php +128 -0
  53. package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/CurrentUserEntity.php +43 -0
  54. package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/EntityQueryBase.php +40 -0
  55. package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/FetchEntity.php +371 -0
  56. package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/FetchString.php +64 -0
  57. package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/FetchTranslatableString.php +74 -0
  58. package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/FocalPoint.php +47 -0
  59. package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/GatsbyBuildId.php +36 -0
  60. package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/GatsbyExtractId.php +28 -0
  61. package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/GatsbyExtractLangcode.php +28 -0
  62. package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/ImageProps.php +149 -0
  63. package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/ListEntities.php +88 -0
  64. package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/ListStrings.php +87 -0
  65. package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/StringId.php +37 -0
  66. package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/StringTranslations.php +94 -0
  67. package/drupal/silverback_gatsby/src/Plugin/GraphQL/Directive/EntityFetch.php +51 -0
  68. package/drupal/silverback_gatsby/src/Plugin/GraphQL/Directive/EntityTranslationsWithDefault.php +30 -0
  69. package/drupal/silverback_gatsby/src/Plugin/GraphQL/Directive/FocalPoint.php +27 -0
  70. package/drupal/silverback_gatsby/src/Plugin/GraphQL/Directive/ImageProps.php +27 -0
  71. package/drupal/silverback_gatsby/src/Plugin/GraphQL/Directive/MenuLangcode.php +27 -0
  72. package/drupal/silverback_gatsby/src/Plugin/GraphQL/Directive/MenuTranslations.php +36 -0
  73. package/drupal/silverback_gatsby/src/Plugin/GraphQL/Directive/SilverbackGatsbyEntityId.php +39 -0
  74. package/drupal/silverback_gatsby/src/Plugin/GraphQL/SchemaExtension/SilverbackGatsbySchemaExtension.php +384 -0
  75. package/drupal/silverback_gatsby/src/SilverbackGatsbyServiceProvider.php +43 -0
  76. package/drupal/silverback_gatsby/src/SilverbackGatsbySessionConfiguration.php +30 -0
  77. package/drupal/silverback_gatsby/src/SilverbackReverseProxyMiddleware.php +49 -0
  78. package/drupal/silverback_gatsby/tests/queries/create-page-fields.gql +12 -0
  79. package/drupal/silverback_gatsby/tests/queries/current-user.gql +6 -0
  80. package/drupal/silverback_gatsby/tests/queries/feed_info.gql +12 -0
  81. package/drupal/silverback_gatsby/tests/queries/load-entity.gql +5 -0
  82. package/drupal/silverback_gatsby/tests/queries/menus.gql +31 -0
  83. package/drupal/silverback_gatsby/tests/queries/multilingual-menus.gql +11 -0
  84. package/drupal/silverback_gatsby/tests/queries/revisionable-translatable.gql +15 -0
  85. package/drupal/silverback_gatsby/tests/queries/revisionable.gql +8 -0
  86. package/drupal/silverback_gatsby/tests/queries/translatable.gql +14 -0
  87. package/drupal/silverback_gatsby/tests/queries/untranslatable.gql +10 -0
  88. package/drupal/silverback_gatsby/tests/schema/.graphqlrc.json +4 -0
  89. package/drupal/silverback_gatsby/tests/schema/translatable-strings.graphql +7 -0
  90. package/drupal/silverback_gatsby/tests/src/Kernel/CurrentUserTest.php +37 -0
  91. package/drupal/silverback_gatsby/tests/src/Kernel/EntityFeedTest.php +490 -0
  92. package/drupal/silverback_gatsby/tests/src/Kernel/EntityFeedTestBase.php +178 -0
  93. package/drupal/silverback_gatsby/tests/src/Kernel/GatsbyBuildTriggerTest.php +155 -0
  94. package/drupal/silverback_gatsby/tests/src/Kernel/GatsbyFeedInfoTest.php +319 -0
  95. package/drupal/silverback_gatsby/tests/src/Kernel/GatsbyUpdateHandlerTest.php +131 -0
  96. package/drupal/silverback_gatsby/tests/src/Kernel/GatsbyUpdateTrackerTest.php +196 -0
  97. package/drupal/silverback_gatsby/tests/src/Kernel/GatsbyUpdateTriggerTest.php +177 -0
  98. package/drupal/silverback_gatsby/tests/src/Kernel/MenuFeedTest.php +192 -0
  99. package/drupal/silverback_gatsby/tests/src/Kernel/TranslatableStringFeedTest.php +210 -0
  100. package/drupal/silverback_gatsby/tests/src/Traits/NotificationCheckTrait.php +43 -0
  101. package/package.json +15 -0
@@ -0,0 +1,139 @@
1
+ <?php
2
+
3
+ namespace Drupal\silverback_gatsby;
4
+
5
+ use Drupal\Core\Menu\MenuTreeParameters;
6
+ use Drupal\Core\Menu\MenuTreeStorageInterface;
7
+ use Drupal\silverback_gatsby\Plugin\Gatsby\Feed\MenuFeed;
8
+
9
+ /**
10
+ * Decorates the menu tree storage and sends updates to Gatsby on modifications.
11
+ */
12
+ class MenuTreeStorageDecorator implements MenuTreeStorageInterface {
13
+
14
+ /**
15
+ * @var \Drupal\Core\Menu\MenuTreeStorageInterface
16
+ */
17
+ protected MenuTreeStorageInterface $subject;
18
+
19
+ /**
20
+ * @var \Drupal\silverback_gatsby\GatsbyUpdateHandler
21
+ */
22
+ protected GatsbyUpdateHandler $updateHandler;
23
+
24
+ /**
25
+ * @param \Drupal\Core\Menu\MenuTreeStorageInterface $subject
26
+ * The decorated menu tree storage service.
27
+ * @param \Drupal\silverback_gatsby\GatsbyUpdateHandler $updateHandler
28
+ * A Gatsby update handler to send events to.
29
+ */
30
+ public function __construct(
31
+ MenuTreeStorageInterface $subject,
32
+ GatsbyUpdateHandler $updateHandler
33
+ ) {
34
+ $this->subject = $subject;
35
+ $this->updateHandler = $updateHandler;
36
+ }
37
+
38
+ /**
39
+ * Send updates after updating a menu item.
40
+ * {@inheritDoc}
41
+ */
42
+ public function save(array $definition) {
43
+ // Emit change events before save to detect if the menu item moved out of
44
+ // a certain level range.
45
+ $this->updateHandler->handle(MenuFeed::class, $definition['id']);
46
+ $this->subject->save($definition);
47
+ // Emit change events after save to update the range it is now in.
48
+ $this->updateHandler->handle(MenuFeed::class, $definition['id']);
49
+ }
50
+
51
+ /**
52
+ * Send updates before deleting a menu item.
53
+ * {@inheritDoc}
54
+ */
55
+ public function delete($id) {
56
+ // Delete update handler has to run before the actual delete operation,
57
+ // because afterwards its position in the tree is lost, and it's not possible
58
+ // to determine which layers are affected.
59
+ $this->updateHandler->handle(MenuFeed::class, $id);
60
+ $this->subject->delete($id);
61
+ }
62
+
63
+ // ======================================================================
64
+ // Untouched public interface.
65
+ // ======================================================================
66
+
67
+ public function maxDepth() {
68
+ return $this->subject->maxDepth();
69
+ }
70
+
71
+ public function resetDefinitions() {
72
+ return $this->subject->resetDefinitions();
73
+ }
74
+
75
+ public function rebuild(array $definitions) {
76
+ return $this->subject->rebuild($definitions);
77
+ }
78
+
79
+ public function load($id) {
80
+ return $this->subject->load($id);
81
+ }
82
+
83
+ public function loadMultiple(array $ids) {
84
+ return $this->subject->loadMultiple($ids);
85
+ }
86
+
87
+ public function loadByProperties(array $properties) {
88
+ return $this->subject->loadByProperties($properties);
89
+ }
90
+
91
+ public function loadByRoute(
92
+ $route_name,
93
+ array $route_parameters = [],
94
+ $menu_name = NULL
95
+ ) {
96
+ return $this->subject->loadByRoute($route_name, $route_parameters, $menu_name);
97
+ }
98
+
99
+ public function loadTreeData($menu_name, MenuTreeParameters $parameters) {
100
+ return $this->subject->loadTreeData($menu_name, $parameters);
101
+ }
102
+
103
+ public function loadAllChildren($id, $max_relative_depth = NULL) {
104
+ return $this->subject->loadAllChildren($id, $max_relative_depth);
105
+ }
106
+
107
+ public function getAllChildIds($id) {
108
+ return $this->subject->getAllChildIds($id);
109
+ }
110
+
111
+ public function loadSubtreeData($id, $max_relative_depth = NULL) {
112
+ return $this->subject->loadSubtreeData($id, $max_relative_depth);
113
+ }
114
+
115
+ public function getRootPathIds($id) {
116
+ return $this->subject->getRootPathIds($id);
117
+ }
118
+
119
+ public function getExpanded($menu_name, array $parents) {
120
+ return $this->subject->getExpanded($menu_name, $parents);
121
+ }
122
+
123
+ public function getSubtreeHeight($id) {
124
+ return $this->subject->getSubtreeHeight($id);
125
+ }
126
+
127
+ public function menuNameInUse($menu_name) {
128
+ return $this->subject->menuNameInUse($menu_name);
129
+ }
130
+
131
+ public function getMenuNames() {
132
+ return $this->subject->getMenuNames();
133
+ }
134
+
135
+ public function countMenuLinks($menu_name = NULL) {
136
+ return $this->subject->countMenuLinks($menu_name);
137
+ }
138
+
139
+ }
@@ -0,0 +1,121 @@
1
+ <?php
2
+
3
+ namespace Drupal\silverback_gatsby\Plugin;
4
+
5
+ use Drupal\Component\Plugin\PluginBase;
6
+ use Drupal\Core\Session\AccountInterface;
7
+ use Drupal\graphql\GraphQL\ResolverBuilder;
8
+ use Drupal\graphql\GraphQL\ResolverRegistryInterface;
9
+ use Drupal\silverback_gatsby\GatsbyUpdate;
10
+ use GraphQL\Language\AST\DocumentNode;
11
+
12
+ /**
13
+ * Base class for Gatsby Data feeds.
14
+ */
15
+ abstract class FeedBase extends PluginBase implements FeedInterface {
16
+
17
+ /**
18
+ * The GraphQL type this feed generates.
19
+ *
20
+ * @var string|mixed
21
+ */
22
+ protected string $typeName;
23
+
24
+ /**
25
+ * The name of a GraphQL field containing the URL path of a node.
26
+ *
27
+ * @var string|null
28
+ */
29
+ protected ?string $pathFieldName;
30
+
31
+ /**
32
+ * The name of a GraphQL field containing the template name for a node.
33
+ *
34
+ * @var string|null
35
+ */
36
+ protected ?string $templateFieldName;
37
+
38
+ /**
39
+ * Instance of a ResolverBuilder.
40
+ *
41
+ * @var \Drupal\graphql\GraphQL\ResolverBuilder
42
+ */
43
+ protected ResolverBuilder $builder;
44
+
45
+ /**
46
+ * FeedBase constructor.
47
+ *
48
+ * @param $config
49
+ * @param $plugin_id
50
+ * @param $plugin_definition
51
+ */
52
+ public function __construct($config, $plugin_id, $plugin_definition) {
53
+ parent::__construct($config, $plugin_id, $plugin_definition);
54
+ $this->builder = new ResolverBuilder();
55
+ $this->typeName = $config['typeName'];
56
+ $this->pathFieldName = $config['createPageFields']['isPath'] ?? NULL;
57
+ $this->templateFieldName = $config['createPageFields']['isTemplate'] ?? NULL;
58
+ }
59
+
60
+ /**
61
+ * {@inheritDoc}
62
+ */
63
+ public function getExtensionDefinition(DocumentNode $parentAst): string {
64
+ return '';
65
+ }
66
+
67
+ /**
68
+ * {@inheritDoc}
69
+ */
70
+ public function addExtensionResolvers(
71
+ ResolverRegistryInterface $registry,
72
+ ResolverBuilder $builder
73
+ ): void {}
74
+
75
+
76
+ public function getTypeName() : string {
77
+ return $this->typeName;
78
+ }
79
+
80
+ public function getSingleFieldName(): string {
81
+ return '_load' . $this->typeName;
82
+ }
83
+
84
+ public function getListFieldName(): string {
85
+ return '_query' . $this->typeName . 's';
86
+ }
87
+
88
+ public function info(): array {
89
+ return [
90
+ 'typeName' => $this->getTypeName(),
91
+ 'translatable' => $this->isTranslatable(),
92
+ 'singleFieldName' => $this->getSingleFieldName(),
93
+ 'listFieldName' => $this->getListFieldName(),
94
+ 'pathFieldName' => $this->pathFieldName,
95
+ 'templateFieldName' => $this->templateFieldName,
96
+ ];
97
+ }
98
+
99
+ /**
100
+ * Investigate the context of an update event and return its id if
101
+ * applicable.
102
+ *
103
+ * @param mixed $context
104
+ * @param \Drupal\Core\Session\AccountInterface|null $account
105
+ *
106
+ * @return string[]
107
+ * A list of string id's to update.
108
+ */
109
+ abstract function getUpdateIds($context, ?AccountInterface $account): array;
110
+
111
+ /**
112
+ * {@inheritDoc}
113
+ */
114
+ public function investigateUpdate($context, ?AccountInterface $account) : array {
115
+ return array_map(
116
+ fn ($id) => new GatsbyUpdate($this->getTypeName(), $id),
117
+ $this->getUpdateIds($context, $account)
118
+ );
119
+ }
120
+
121
+ }
@@ -0,0 +1,132 @@
1
+ <?php
2
+
3
+ namespace Drupal\silverback_gatsby\Plugin;
4
+
5
+ use Drupal\Core\Session\AccountInterface;
6
+ use Drupal\graphql\GraphQL\Resolver\ResolverInterface;
7
+ use Drupal\graphql\GraphQL\ResolverBuilder;
8
+ use Drupal\graphql\GraphQL\ResolverRegistryInterface;
9
+ use GraphQL\Language\AST\DocumentNode;
10
+
11
+ /**
12
+ * Interface FeedInterface
13
+ *
14
+ * Describes a single data-feed that is sent to Gatsby.
15
+ *
16
+ * @package Drupal\silverback_gatsby
17
+ */
18
+ interface FeedInterface {
19
+
20
+ /**
21
+ * Retrieve feed meta information used by the GraphQL schema.
22
+ *
23
+ * @return array
24
+ */
25
+ public function info() : array;
26
+
27
+ /**
28
+ * Define extension schema definitions this Feed provides.
29
+ *
30
+ * @param \GraphQL\Language\AST\DocumentNode $parentAst
31
+ *
32
+ * @return string
33
+ */
34
+ public function getExtensionDefinition(DocumentNode $parentAst) : string;
35
+
36
+ /**
37
+ * Build resolvers for the defined schema definitions.
38
+ *
39
+ * @param \Drupal\graphql\GraphQL\ResolverRegistryInterface $registry
40
+ * @param \Drupal\graphql\GraphQL\ResolverBuilder $builder
41
+ */
42
+ public function addExtensionResolvers(ResolverRegistryInterface $registry, ResolverBuilder $builder) : void;
43
+
44
+ /**
45
+ * Decide if a type is translatable or not.
46
+ *
47
+ * @return bool
48
+ */
49
+ public function isTranslatable() : bool;
50
+
51
+ /**
52
+ * Investigate an arbitrary context value and produce a GatsbyUpdate if
53
+ * applicable.
54
+ *
55
+ * @param $context
56
+ * @param \Drupal\Core\Session\AccountInterface|null $account
57
+ *
58
+ * @return \Drupal\silverback_gatsby\GatsbyUpdate[]
59
+ */
60
+ public function investigateUpdate($context, ?AccountInterface $account) : array;
61
+
62
+ /**
63
+ * Resolve the parent value's "id" attribute.
64
+ *
65
+ * @return \Drupal\graphql\GraphQL\Resolver\ResolverInterface
66
+ */
67
+ public function resolveId() : ResolverInterface;
68
+
69
+ /**
70
+ * Resolve the parent value's language association.
71
+ *
72
+ * @return \Drupal\graphql\GraphQL\Resolver\ResolverInterface
73
+ */
74
+ public function resolveLangcode() : ResolverInterface;
75
+
76
+
77
+ /**
78
+ * Determine if the item is the default translation.
79
+ *
80
+ * @return \Drupal\graphql\GraphQL\Resolver\ResolverInterface
81
+ */
82
+ public function resolveDefaultTranslation() : ResolverInterface;
83
+
84
+ /**
85
+ * Resolve a list of all available translations.
86
+ *
87
+ * @return \Drupal\graphql\GraphQL\Resolver\ResolverInterface
88
+ */
89
+ public function resolveTranslations() : ResolverInterface;
90
+
91
+ /**
92
+ * Resolve a paginated list of all items of this type.
93
+ *
94
+ * @param \Drupal\graphql\GraphQL\Resolver\ResolverInterface $limit
95
+ * @param \Drupal\graphql\GraphQL\Resolver\ResolverInterface $offset
96
+ *
97
+ * @return \Drupal\graphql\GraphQL\Resolver\ResolverInterface
98
+ */
99
+ public function resolveItems(ResolverInterface $limit, ResolverInterface $offset) : ResolverInterface;
100
+
101
+ /**
102
+ * Load a single item by ID.
103
+ *
104
+ * @param \Drupal\graphql\GraphQL\Resolver\ResolverInterface $id
105
+ * @param \Drupal\graphql\GraphQL\Resolver\ResolverInterface|null $langcode
106
+ *
107
+ * @return \Drupal\graphql\GraphQL\Resolver\ResolverInterface
108
+ */
109
+ public function resolveItem(ResolverInterface $id, ?ResolverInterface $langcode = null) : ResolverInterface;
110
+
111
+
112
+ /**
113
+ * Retrieve the GraphQL type name this feed emits.
114
+ *
115
+ * @return string
116
+ */
117
+ public function getTypeName() : string;
118
+
119
+ /**
120
+ * Retrieve the field name to load a single item.
121
+ *
122
+ * @return string
123
+ */
124
+ public function getSingleFieldName() : string;
125
+
126
+ /**
127
+ * Retrieve the field name to fetch multiple items via pagination.
128
+ *
129
+ * @return string
130
+ */
131
+ public function getListFieldName() : string;
132
+ }
@@ -0,0 +1,39 @@
1
+ <?php
2
+
3
+ namespace Drupal\silverback_gatsby\Plugin;
4
+
5
+ use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
6
+ use Drupal\Core\Plugin\DefaultPluginManager;
7
+
8
+ /**
9
+ * Class FeedPluginManager
10
+ *
11
+ * @package Drupal\silverback_gatsby\Plugin
12
+ */
13
+ class FeedPluginManager extends DefaultPluginManager {
14
+
15
+ protected function findDefinitions() {
16
+ $definitions = parent::findDefinitions();
17
+
18
+ // Attach the directive definition for each feed plugin so it is available
19
+ // along with the plugin definition.
20
+ foreach ($definitions as $id => $def) {
21
+ $module = \Drupal::moduleHandler()->getModule($def['provider']);
22
+ $path = 'graphql/' . $id . '.directive.graphqls';
23
+ $file = $module->getPath() . '/' . $path;
24
+
25
+ if (!file_exists($file)) {
26
+ throw new InvalidPluginDefinitionException(
27
+ $id,
28
+ sprintf('The module "%s" needs to have a directive definition "%s" in its folder for "%s" to be valid.',
29
+ $module->getName(), $path, $id));
30
+ }
31
+
32
+ $definitions[$id]['directive'] = file_get_contents($file);
33
+ }
34
+
35
+ return $definitions;
36
+ }
37
+
38
+
39
+ }
@@ -0,0 +1,252 @@
1
+ <?php
2
+
3
+ namespace Drupal\silverback_gatsby\Plugin\Gatsby\Feed;
4
+
5
+ use Drupal\content_translation\ContentTranslationManagerInterface;
6
+ use Drupal\Core\Entity\EntityInterface;
7
+ use Drupal\Core\Entity\TranslatableInterface;
8
+ use Drupal\Core\Language\LanguageInterface;
9
+ use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
10
+ use Drupal\Core\Session\AccountInterface;
11
+ use Drupal\graphql\GraphQL\Resolver\ResolverInterface;
12
+ use Drupal\graphql\GraphQL\ResolverBuilder;
13
+ use Drupal\graphql\GraphQL\ResolverRegistryInterface;
14
+ use Drupal\node\Entity\Node;
15
+ use Drupal\path_alias\AliasManagerInterface;
16
+ use Drupal\path_alias\Entity\PathAlias;
17
+ use Drupal\silverback_gatsby\Plugin\FeedBase;
18
+ use Drupal\silverback_gatsby\Plugin\GraphQL\DataProducer\GatsbyBuildId;
19
+ use GraphQL\Language\AST\DocumentNode;
20
+ use Symfony\Component\DependencyInjection\ContainerInterface;
21
+
22
+ /**
23
+ * Feed plugin that creates Gatsby feeds based on Drupal entities.
24
+ *
25
+ * @GatsbyFeed(
26
+ * id = "entity"
27
+ * )
28
+ */
29
+ class EntityFeed extends FeedBase implements ContainerFactoryPluginInterface {
30
+
31
+ /**
32
+ * The target entity type.
33
+ *
34
+ * @var string
35
+ */
36
+ protected string $type;
37
+
38
+ /**
39
+ * The target entity bundle.
40
+ *
41
+ * @var string|null
42
+ */
43
+ protected ?string $bundle;
44
+
45
+ /**
46
+ * Indicates if Drupal access restrictions should be respected.
47
+ *
48
+ * Defaults to true.
49
+ *
50
+ * @var bool
51
+ */
52
+ protected bool $access;
53
+
54
+ /**
55
+ * @var \Drupal\content_translation\ContentTranslationManagerInterface|null
56
+ */
57
+ protected ?ContentTranslationManagerInterface $contentTranslationManager;
58
+
59
+ /**
60
+ * The path alias manager.
61
+ *
62
+ * @var \Drupal\path_alias\AliasManagerInterface
63
+ */
64
+ protected $pathAliasManager;
65
+
66
+ /**
67
+ * {@inheritDoc}
68
+ */
69
+ public static function create(
70
+ ContainerInterface $container,
71
+ array $configuration,
72
+ $plugin_id,
73
+ $plugin_definition
74
+ ) {
75
+ return new static(
76
+ $configuration,
77
+ $plugin_id,
78
+ $plugin_definition,
79
+ $container->has('content_translation.manager') ? $container->get('content_translation.manager') : NULL,
80
+ $container->get('path_alias.manager')
81
+ );
82
+ }
83
+
84
+ public function __construct(
85
+ $config,
86
+ $plugin_id,
87
+ $plugin_definition,
88
+ ?ContentTranslationManagerInterface $contentTranslationManager,
89
+ AliasManagerInterface $path_alias_manager
90
+ ) {
91
+ $this->type = $config['type'];
92
+ $this->bundle = $config['bundle'] ?? NULL;
93
+ $this->access = $config['access'] ?? TRUE;
94
+ $this->contentTranslationManager = $contentTranslationManager;
95
+ $this->pathAliasManager = $path_alias_manager;
96
+
97
+ parent::__construct(
98
+ $config,
99
+ $plugin_id,
100
+ $plugin_definition
101
+ );
102
+ }
103
+
104
+ /**
105
+ * {@inheritDoc}
106
+ */
107
+ public function isTranslatable(): bool {
108
+ return $this->contentTranslationManager &&
109
+ $this->contentTranslationManager->isEnabled($this->type, $this->bundle);
110
+ }
111
+
112
+ /**
113
+ * {@inheritDoc}
114
+ */
115
+ public function getUpdateIds($context, ?AccountInterface $account) : array {
116
+
117
+ // Special case for path alias.
118
+ if ($context instanceof PathAlias) {
119
+ $path = $this->pathAliasManager->getPathByAlias($context->alias->value);
120
+ if (preg_match('/node\/(\d+)/', $path, $matches)) {
121
+ $node = isset($matches[1]) ? Node::load($matches[1]) : NULL;
122
+ if ($node) {
123
+ // See SLB-281 for details.
124
+ $context = $node;
125
+ }
126
+ }
127
+ }
128
+
129
+ if (
130
+ $context instanceof EntityInterface
131
+ && $context->getEntityTypeId() === $this->type
132
+ && ($this->bundle !== NULL && $context->bundle() === $this->bundle)
133
+ && (!$account || $context->access('view', $account))
134
+ ) {
135
+ if ($this->isTranslatable() && $context instanceof TranslatableInterface) {
136
+ return array_map(function (LanguageInterface $lang) use ($context) {
137
+ $translation = $context->getTranslation($lang->getId());
138
+ return GatsbyBuildId::build($context->uuid(), $translation->language()->getId());
139
+ }, $context->getTranslationLanguages());
140
+ }
141
+ return [$context->uuid()];
142
+ }
143
+ return [];
144
+ }
145
+
146
+ /**
147
+ * {@inheritDoc}
148
+ */
149
+ public function resolveItem(ResolverInterface $id, ?ResolverInterface $langcode = NULL): ResolverInterface {
150
+ $resolver = $this->builder->produce('fetch_entity')
151
+ ->map('type', $this->builder->fromValue($this->type))
152
+ ->map('bundles', $this->builder->fromValue($this->bundle === NULL ? NULL : [$this->bundle]))
153
+ ->map('access', $this->builder->fromValue($this->access))
154
+ ->map('id', $id);
155
+ if ($this->isTranslatable() && $langcode) {
156
+ $resolver->map('language', $langcode);
157
+ }
158
+ return $resolver;
159
+ }
160
+
161
+ /**
162
+ * {@inheritDoc}
163
+ */
164
+ public function resolveItems(ResolverInterface $limit, ResolverInterface $offset): ResolverInterface {
165
+ return $this->builder->produce('list_entities')
166
+ ->map('type', $this->builder->fromValue($this->type))
167
+ ->map('bundle', $this->builder->fromValue($this->bundle))
168
+ ->map('access', $this->builder->fromValue($this->access))
169
+ ->map('offset', $this->builder->defaultValue(
170
+ $this->builder->fromArgument('offset'),
171
+ $this->builder->fromValue(0)
172
+ ))
173
+ ->map('limit', $this->builder->defaultValue(
174
+ $this->builder->fromArgument('limit'),
175
+ $this->builder->fromValue(10),
176
+ ));
177
+ }
178
+
179
+ /**
180
+ * {@inheritDoc}
181
+ */
182
+ public function resolveId(): ResolverInterface {
183
+ return $this->builder->produce('entity_uuid')
184
+ ->map('entity', $this->builder->fromParent());
185
+ }
186
+
187
+ /**
188
+ * {@inheritDoc}
189
+ */
190
+ public function resolveLangcode(): ResolverInterface {
191
+ return $this->builder->callback(
192
+ fn(TranslatableInterface $value) => $value->language()->getId()
193
+ );
194
+ }
195
+
196
+ /**
197
+ * {@inheritDoc}
198
+ */
199
+ public function resolveDefaultTranslation(): ResolverInterface {
200
+ return $this->builder->callback(
201
+ fn(TranslatableInterface $value) => $value->isDefaultTranslation()
202
+ );
203
+ }
204
+
205
+ /**
206
+ * {@inheritDoc}
207
+ */
208
+ public function resolveTranslations(): ResolverInterface {
209
+ return $this->builder->defaultValue(
210
+ $this->builder->compose(
211
+ $this->builder->produce('entity_translations')->map('entity', $this->builder->fromParent()),
212
+ // entity_translations returns nulls for inaccessible translations. This
213
+ // does not match our schema. Filter nulls out.
214
+ $this->builder->callback(fn ($entities) => $entities ? array_filter($entities) : NULL)
215
+ ),
216
+ $this->builder->callback(fn ($value) => [$value])
217
+ );
218
+ }
219
+
220
+ /**
221
+ *
222
+ */
223
+ public function getExtensionDefinition(DocumentNode $parentAst): string {
224
+ $type = $this->typeName;
225
+ return "\nextend type Query { _load{$type}Revision(id: String!, revision: String!) : $type @deprecated }";
226
+ }
227
+
228
+ /**
229
+ *
230
+ */
231
+ public function addExtensionResolvers(
232
+ ResolverRegistryInterface $registry,
233
+ ResolverBuilder $builder
234
+ ): void {
235
+ $type = $this->typeName;
236
+ $idResolver = $this->isTranslatable()
237
+ ? $builder->produce('gatsby_extract_id')->map('id', $builder->fromArgument('id'))
238
+ : $builder->fromArgument('id');
239
+ $langResolver = $this->isTranslatable()
240
+ ? $builder->produce('gatsby_extract_langcode')->map('id', $builder->fromArgument('id'))
241
+ : $builder->fromValue(NULL);
242
+ $resolver = $this->builder->produce('fetch_entity')
243
+ ->map('type', $this->builder->fromValue($this->type))
244
+ ->map('bundles', $this->builder->fromValue($this->bundle === NULL ? NULL : [$this->bundle]))
245
+ ->map('access', $this->builder->fromValue($this->access))
246
+ ->map('id', $idResolver)
247
+ ->map('language', $langResolver)
248
+ ->map('revision_id', $builder->fromArgument('revision'));
249
+ $registry->addFieldResolver("Query", "_load{$type}Revision", $resolver);
250
+ }
251
+
252
+ }