@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,230 @@
1
+ <?php
2
+
3
+ namespace Drupal\silverback_gatsby;
4
+
5
+ use Drupal\Core\Entity\EntityTypeManagerInterface;
6
+ use Drupal\Core\Messenger\MessengerInterface;
7
+ use Drupal\Core\StringTranslation\StringTranslationTrait;
8
+ use Drupal\Core\StringTranslation\TranslatableMarkup;
9
+ use Drupal\graphql\Entity\ServerInterface;
10
+ use GuzzleHttp\Client;
11
+ use GuzzleHttp\Cookie\CookieJar;
12
+ use GuzzleHttp\Exception\GuzzleException;
13
+ use GuzzleHttp\RequestOptions;
14
+
15
+ class GatsbyBuildTrigger implements GatsbyBuildTriggerInterface {
16
+
17
+ use StringTranslationTrait;
18
+
19
+ protected array $buildIds = [];
20
+ protected Client $httpClient;
21
+ protected MessengerInterface $messenger;
22
+ protected EntityTypeManagerInterface $entityTypeManager;
23
+
24
+ /**
25
+ * GatsbyBuildTriggerDecorator constructor.
26
+ *
27
+ * @param \GuzzleHttp\Client $httpClient
28
+ * @param \Drupal\Core\Messenger\MessengerInterface $messenger
29
+ * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
30
+ */
31
+ public function __construct(
32
+ Client $httpClient,
33
+ MessengerInterface $messenger,
34
+ EntityTypeManagerInterface $entityTypeManager
35
+ ) {
36
+ $this->messenger = $messenger;
37
+ $this->httpClient = $httpClient;
38
+ $this->entityTypeManager = $entityTypeManager;
39
+ }
40
+
41
+ /**
42
+ * {@inheritDoc}
43
+ */
44
+ public function trigger(string $server, int $id) : void {
45
+ // Make sure the shutdown function is registered only once.
46
+ if (!$this->buildIds) {
47
+ drupal_register_shutdown_function([$this, 'sendUpdates']);
48
+ }
49
+ if ($url = $this->getWebhook($server)) {
50
+ $this->buildIds[$url] = $id;
51
+ }
52
+ }
53
+
54
+ /**
55
+ * {@inheritDoc}
56
+ */
57
+ public function triggerLatestBuild(string $server) : TranslatableMarkup {
58
+ $message = $this->t('No server found with id @server_id.', [
59
+ '@server_id' => $server,
60
+ ]);
61
+
62
+ $servers = $this->entityTypeManager
63
+ ->getStorage('graphql_server')
64
+ ->loadByProperties(['name' => $server]);
65
+
66
+ if (empty($servers)) {
67
+ $message = $this->t('No server found with id @server_id.', [
68
+ '@server_id' => $server,
69
+ ]);
70
+ $this->messenger->addError($message);
71
+ return $message;
72
+ }
73
+
74
+ $serverEntity = reset($servers);
75
+ if ($serverEntity instanceof ServerInterface && is_string($serverEntity->id())) {
76
+ // No dependency injection, prevent circular reference.
77
+ /** @var \Drupal\silverback_gatsby\GatsbyUpdateTrackerInterface $updateTracker */
78
+ $updateTracker = \Drupal::service('silverback_gatsby.update_tracker');
79
+ $latestBuildId = $updateTracker->latestBuild($serverEntity->id());
80
+ if (!$this->isFrontendLatestBuild($latestBuildId, $serverEntity)) {
81
+ $message = $this->t('Triggering a build for server @server_id.', [
82
+ '@server_id' => $server,
83
+ ]);
84
+ $this->messenger->addStatus($message);
85
+ $this->trigger($serverEntity->id(), $latestBuildId);
86
+ }
87
+ else {
88
+ $message = $this->t('Build is already up-to-date for server @server_id.', [
89
+ '@server_id' => $server,
90
+ ]);
91
+ $this->messenger->addStatus($message);
92
+ }
93
+ }
94
+
95
+ return $message;
96
+ }
97
+
98
+ /**
99
+ * {@inheritDoc}
100
+ */
101
+ public function triggerDefaultServerLatestBuild() : TranslatableMarkup {
102
+ $servers = $this->entityTypeManager->getStorage('graphql_server')->loadMultiple();
103
+ $silverbackGatsbyServers = array_filter($servers, function (ServerInterface $server) {
104
+ $configuration = $server->get('schema_configuration')[$server->get('schema')];
105
+ return !empty($configuration['extensions']['silverback_gatsby']);
106
+ });
107
+ $silverbackGatsbyServer = reset($silverbackGatsbyServers);
108
+
109
+ if ($silverbackGatsbyServer instanceof ServerInterface && is_string($silverbackGatsbyServer->id())) {
110
+ return $this->triggerLatestBuild($silverbackGatsbyServer->id());
111
+ }
112
+
113
+ $message = $this->t('No default server found.');
114
+ $this->messenger->addError($message);
115
+ return $message;
116
+ }
117
+
118
+ /**
119
+ * Check on the frontend if the latest build already occurred.
120
+ *
121
+ * If the build url is not configured, presume false so the build
122
+ * will still happen.
123
+ *
124
+ * @param int $latestBuildId
125
+ * The latest build id.
126
+ * @param \Drupal\graphql\Entity\ServerInterface $serverEntity
127
+ *
128
+ * @return bool
129
+ * Whether the frontend is already up-to-date.
130
+ *
131
+ * @throws \GuzzleHttp\Exception\GuzzleException
132
+ */
133
+ protected function isFrontendLatestBuild(int $latestBuildId, ServerInterface $serverEntity): bool {
134
+ $result = FALSE;
135
+ $configuration = $serverEntity->get('schema_configuration')[$serverEntity->get('schema')];
136
+ if (!empty($configuration['build_url'])) {
137
+ $buildJsonUrl = $configuration['build_url'] . '/build.json';
138
+ $httpOptions = [
139
+ RequestOptions::HTTP_ERRORS => FALSE,
140
+ RequestOptions::COOKIES => new CookieJar(),
141
+ ];
142
+ $response = $this->httpClient->get($buildJsonUrl, $httpOptions);
143
+ if (
144
+ $response->getStatusCode() === 401 &&
145
+ !empty($configuration['build_url_netlify_password'])
146
+ ) {
147
+ $response = $this->httpClient->post($buildJsonUrl, $httpOptions + [
148
+ RequestOptions::FORM_PARAMS => [
149
+ 'password' => $configuration['build_url_netlify_password'],
150
+ ],
151
+ ]);
152
+ }
153
+ if ($response->getStatusCode() === 200) {
154
+ $contents = $response->getBody()->getContents();
155
+
156
+ if (empty($contents)) {
157
+ return FALSE;
158
+ }
159
+
160
+ $content = json_decode($contents, TRUE);
161
+ $buildId = array_key_exists('drupalBuildId', $content) ? (int) $content['drupalBuildId'] : 0;
162
+ $result = $latestBuildId === $buildId;
163
+ }
164
+ }
165
+ return $result;
166
+ }
167
+
168
+ /**
169
+ * Get the webhook URL for a server.
170
+ *
171
+ * @param string $server_id
172
+ * The server id.
173
+ *
174
+ * @return mixed|null
175
+ * The webhook URL or NULL.
176
+ *
177
+ * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
178
+ * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
179
+ */
180
+ protected function getWebhook(string $server_id): mixed {
181
+ $server = $this->entityTypeManager
182
+ ->getStorage('graphql_server')
183
+ ->load($server_id);
184
+
185
+ if (!$server instanceof ServerInterface) {
186
+ return NULL;
187
+ }
188
+
189
+ $schema_id = $server->get('schema');
190
+
191
+ if (isset($server->get('schema_configuration')[$schema_id]['build_webhook'])) {
192
+ return $server->get('schema_configuration')[$schema_id]['build_webhook'];
193
+ }
194
+
195
+ return NULL;
196
+ }
197
+
198
+ /**
199
+ * {@inheritDoc}
200
+ */
201
+ public function sendUpdates() : void {
202
+ foreach ($this->buildIds as $url => $id) {
203
+ try {
204
+ $this->httpClient->post($url, [
205
+ 'headers' => [
206
+ // We have to pretend not to be Drupal, or Gatsby cloud will
207
+ // apply "magic" that causes it not to re-run our sourceNodes
208
+ // function.
209
+ 'User-Agent' => 'CMS',
210
+ ],
211
+ 'json' => ['buildId' => $id],
212
+ // It can happen that a request hangs for too long time.
213
+ 'timeout' => 2,
214
+ ]);
215
+ if (
216
+ function_exists('_gatsby_build_monitor_state') &&
217
+ // This detects the "build" webhook.
218
+ str_contains($url, '/data_source/publish/')
219
+ ) {
220
+ _gatsby_build_monitor_state('building');
221
+ }
222
+ }
223
+ catch (\Exception | GuzzleException $exc) {
224
+ $this->messenger->addError('Could not send build notification to server "' . $url . '".');
225
+ $this->messenger->addError($exc->getMessage());
226
+ }
227
+ }
228
+ }
229
+
230
+ }
@@ -0,0 +1,52 @@
1
+ <?php
2
+
3
+ namespace Drupal\silverback_gatsby;
4
+
5
+ use Drupal\Core\StringTranslation\TranslatableMarkup;
6
+
7
+ interface GatsbyBuildTriggerInterface {
8
+
9
+ /**
10
+ * Trigger a build for a given server with a build id.
11
+ *
12
+ * @param string $server
13
+ * The server id.
14
+ * @param int $id
15
+ * The build id.
16
+ */
17
+ public function trigger(string $server, int $id) : void;
18
+
19
+ /**
20
+ * Trigger a build for a given server based on latest build id.
21
+ *
22
+ * Compares the latest build id with the one on the frontend to not
23
+ * trigger unnecessary builds.
24
+ *
25
+ * @param string $server
26
+ * The server id.
27
+ *
28
+ * @return TranslatableMarkup
29
+ * The result message.
30
+ */
31
+ public function triggerLatestBuild(string $server) : TranslatableMarkup;
32
+
33
+ /**
34
+ * Trigger a build for the default server.
35
+ *
36
+ * The default server is selected based on the enabled silverback_gatsby
37
+ * extension. If there are multiple, the first one is selected.
38
+ *
39
+ * Compares the latest build id with the one on the frontend to not
40
+ * trigger unnecessary builds.
41
+ *
42
+ * @return \Drupal\Core\StringTranslation\TranslatableMarkup
43
+ * The result message.
44
+ */
45
+ public function triggerDefaultServerLatestBuild() : TranslatableMarkup;
46
+
47
+ /**
48
+ * Send out notifications about potential updates to all Gatsby servers.
49
+ */
50
+ public function sendUpdates() : void;
51
+
52
+ }
@@ -0,0 +1,38 @@
1
+ <?php
2
+
3
+ namespace Drupal\silverback_gatsby;
4
+
5
+ /**
6
+ * Class GatsbyUpdate
7
+ *
8
+ * Simple struct to represent a Gatsby update event.
9
+ *
10
+ * @package Drupal\silverback_gatsby
11
+ */
12
+ class GatsbyUpdate {
13
+
14
+ /**
15
+ * @var string $type
16
+ * The GraphQL type that was updated.
17
+ */
18
+ public string $type;
19
+
20
+ /**
21
+ * @var string $id
22
+ * The GraphQL object id that was updated.
23
+ */
24
+ public string $id;
25
+
26
+ /**
27
+ * GatsbyUpdate constructor.
28
+ *
29
+ * @param string $type
30
+ * The GraphQL type name.
31
+ * @param string $id
32
+ * The GraphQL object id.
33
+ */
34
+ public function __construct(string $type, string $id) {
35
+ $this->type = $type;
36
+ $this->id = $id;
37
+ }
38
+ }
@@ -0,0 +1,181 @@
1
+ <?php
2
+
3
+ namespace Drupal\silverback_gatsby;
4
+
5
+ use Drupal\Core\Entity\EntityTypeManagerInterface;
6
+ use Drupal\graphql_directives\Plugin\GraphQL\Schema\DirectableSchema;
7
+ use Drupal\silverback_gatsby\Plugin\GraphQL\SchemaExtension\SilverbackGatsbySchemaExtension;
8
+ use Drupal\user\Entity\User;
9
+
10
+ /**
11
+ * Class GatsbyUpdateHandler
12
+ *
13
+ * Handle update events from different sources, process and distribute them.
14
+ *
15
+ * @package Drupal\silverback_gatsby
16
+ */
17
+ class GatsbyUpdateHandler {
18
+
19
+ /**
20
+ * @var array<array{
21
+ * account: \Drupal\user\UserInterface,
22
+ * feeds: array<\Drupal\silverback_gatsby\Plugin\FeedInterface>,
23
+ * serverId: string,
24
+ * trigger: bool
25
+ * }>|null
26
+ * @internal
27
+ */
28
+ public ?array $schemaCache = NULL;
29
+
30
+ protected EntityTypeManagerInterface $entityTypeManager;
31
+ protected GatsbyUpdateTrackerInterface $gatsbyUpdateTracker;
32
+ protected GatsbyUpdateTriggerInterface $gatsbyUpdateTrigger;
33
+
34
+ public function __construct(
35
+ EntityTypeManagerInterface $entityTypeManager,
36
+ GatsbyUpdateTrackerInterface $gatsbyUpdateTracker,
37
+ GatsbyUpdateTriggerInterface $gatsbyUpdateTrigger
38
+ ) {
39
+ $this->entityTypeManager = $entityTypeManager;
40
+ $this->gatsbyUpdateTracker = $gatsbyUpdateTracker;
41
+ $this->gatsbyUpdateTrigger = $gatsbyUpdateTrigger;
42
+ }
43
+
44
+ /**
45
+ * Handle an update event.
46
+ *
47
+ * @param string $feedClassName
48
+ * The feed class name.
49
+ * @param $context
50
+ * The context value.
51
+ *
52
+ * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
53
+ * @throws \Drupal\Component\Plugin\Exception\PluginException
54
+ * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
55
+ * @throws \GraphQL\Error\SyntaxError
56
+ */
57
+ public function handle(string $feedClassName, $context) {
58
+ foreach ($this->getSchemas() as $schema) {
59
+ foreach ($schema['feeds'] as $feed) {
60
+ if (
61
+ $feed instanceof $feedClassName
62
+ ) {
63
+ // Updates for the actual build. They should respect access
64
+ // permissions of the configured account.
65
+ if ($updates = $feed->investigateUpdate($context, $schema['account'])) {
66
+ foreach ($updates as $update) {
67
+ $this->gatsbyUpdateTracker->track(
68
+ $schema['serverId'],
69
+ $update->type,
70
+ $update->id,
71
+ $schema['trigger'],
72
+ );
73
+ }
74
+ }
75
+ // Preview change notifications should always go through.
76
+ if ($changes = $feed->investigateUpdate($context, null)) {
77
+ foreach ($changes as $change) {
78
+ $this->gatsbyUpdateTrigger->trigger(
79
+ $schema['serverId'],
80
+ $change
81
+ );
82
+ }
83
+ }
84
+ }
85
+ }
86
+ }
87
+ }
88
+
89
+ /**
90
+ * @return array<array{
91
+ * account: \Drupal\user\UserInterface,
92
+ * feeds: array<\Drupal\silverback_gatsby\Plugin\FeedInterface>,
93
+ * serverId: string,
94
+ * trigger: bool
95
+ * }>|null
96
+ */
97
+ protected function getSchemas(): array {
98
+ if ($this->schemaCache !== NULL) {
99
+ return $this->schemaCache;
100
+ }
101
+ $this->schemaCache = [];
102
+
103
+ /** @var \Drupal\graphql\Entity\ServerInterface[] $servers */
104
+ $servers = $this->entityTypeManager
105
+ ->getStorage('graphql_server')
106
+ ->loadMultiple();
107
+
108
+ $manager = \Drupal::service('plugin.manager.graphql.schema');
109
+
110
+ /** @var \Drupal\graphql\Plugin\SchemaPluginInterface $plugin */
111
+ foreach($servers as $server) {
112
+ $schema_id = $server->get('schema');
113
+ $schema = $manager->createInstance($schema_id);
114
+ if ($schema instanceof DirectableSchema && $config = $server->get('schema_configuration')) {
115
+ if (!isset($config[$schema_id]['build_webhook'])) {
116
+ continue;
117
+ }
118
+ $schema->setConfiguration($config[$schema_id] ?? []);
119
+
120
+ /**
121
+ * @var array{
122
+ * account: \Drupal\user\UserInterface,
123
+ * feeds: array<\Drupal\silverback_gatsby\Plugin\FeedInterface>,
124
+ * serverId: string,
125
+ * trigger: bool
126
+ * } $item
127
+ * */
128
+ $item = [];
129
+
130
+ if ($config[$schema_id]['user'] ?? NULL) {
131
+ $accounts = $this->entityTypeManager->getStorage('user')->loadByProperties(['uuid' => $config[$schema_id]['user']]);
132
+ $account = reset($accounts);
133
+ if (!$account) {
134
+ if (!getenv('SB_SETUP')) {
135
+ \Drupal::messenger()->addError("The website won't be rebuilt because of a misconfigured GraphQL server (missing user)");
136
+ \Drupal::logger('silverback_gatsby')->error('Cannot load user "{user}" configured in server "{server}".', [
137
+ 'user' => $config[$schema_id]['user'],
138
+ 'server' => $server->id(),
139
+ ]);
140
+ }
141
+ return $this->schemaCache;
142
+ }
143
+ }
144
+ else {
145
+ // This is deprecated. It causes issues with the domain module:
146
+ // https://github.com/AmazeeLabs/silverback-mono/issues/928
147
+ $account = User::create();
148
+ if (isset($config[$schema_id]['role'])) {
149
+ $account->addRole($config[$schema_id]['role']);
150
+ }
151
+ }
152
+ $item['account'] = $account;
153
+
154
+ $item['serverId'] = $server->id();
155
+
156
+ // If the configuration is not set, assume TRUE.
157
+ $trigger = TRUE;
158
+ if (array_key_exists('build_trigger_on_save', $config[$schema_id])) {
159
+ $trigger = $config[$schema_id]['build_trigger_on_save'] === 1;
160
+ }
161
+ $item['trigger'] = $trigger;
162
+
163
+ $item['feeds'] = [];
164
+ $extensions = $schema->getExtensions();
165
+ $ast = $schema->getSchemaDocument($extensions);
166
+ foreach ($extensions as $extension) {
167
+ if ($extension instanceof SilverbackGatsbySchemaExtension) {
168
+ foreach ($extension->getFeeds($ast) as $feed) {
169
+ $item['feeds'][] = $feed;
170
+ }
171
+ }
172
+ }
173
+
174
+ $this->schemaCache[] = $item;
175
+ }
176
+ }
177
+
178
+ return $this->schemaCache;
179
+ }
180
+
181
+ }
@@ -0,0 +1,98 @@
1
+ <?php
2
+
3
+ namespace Drupal\silverback_gatsby;
4
+
5
+ use Drupal\Core\Database\Connection;
6
+ use Drupal\Core\Session\AccountInterface;
7
+
8
+ /**
9
+ * Class GatsbyUpdateTracker
10
+ *
11
+ * Track, store and retrieve updates that happened concerning
12
+ *
13
+ * @package Drupal\silverback_gatsby
14
+ */
15
+ class GatsbyUpdateTracker implements GatsbyUpdateTrackerInterface {
16
+ protected Connection $database;
17
+ protected AccountInterface $currentUser;
18
+ protected GatsbyBuildTriggerInterface $trigger;
19
+ protected array $tracked;
20
+
21
+ public function __construct(
22
+ Connection $database,
23
+ AccountInterface $currentUser,
24
+ GatsbyBuildTriggerInterface $trigger
25
+ ) {
26
+ $this->tracked = [];
27
+ $this->database = $database;
28
+ $this->currentUser = $currentUser;
29
+ $this->trigger = $trigger;
30
+ }
31
+
32
+ /**
33
+ * {@inheritDoc}
34
+ */
35
+ public function clear() : void {
36
+ $this->tracked = [];
37
+ }
38
+
39
+ /**
40
+ * {@inheritDoc}
41
+ */
42
+ public function track(string $server, string $type, string $id, bool $trigger = TRUE) : int {
43
+ if (isset($this->tracked[$server]) && isset($this->tracked[$server][$type]) && in_array($id,$this->tracked[$server][$type])) {
44
+ return $this->latestBuild($server);
45
+ }
46
+ $this->tracked[$server][$type][] = $id;
47
+ $buildId = $this->database->insert('gatsby_update_log')->fields([
48
+ 'server' => $server,
49
+ 'type' => $type,
50
+ 'object_id' => $id,
51
+ 'uid' => $this->currentUser->id(),
52
+ 'timestamp' => time(),
53
+ ])->execute();
54
+ if ($trigger) {
55
+ $this->trigger->trigger($server, $buildId);
56
+ }
57
+ return $buildId;
58
+ }
59
+
60
+ /**
61
+ * {@inheritDoc}
62
+ */
63
+ public function diff(int $lastBuild, int $currentBuild, string $server) : array {
64
+ $count = intval($this->database->select('gatsby_update_log', 'gul')
65
+ ->condition('server', $server)
66
+ ->condition('id', [$lastBuild, $currentBuild], 'IN')
67
+ ->countQuery()
68
+ ->execute()
69
+ ->fetchField());
70
+
71
+ // If either of the two builds does not exist, we can't be sure about
72
+ // history consistency and return an empty list which will trigger a full
73
+ // rebuild.
74
+ if ($count < 2) {
75
+ return [];
76
+ }
77
+
78
+ return array_map(fn ($row) => new GatsbyUpdate($row->type, $row->object_id), $this->database
79
+ ->select('gatsby_update_log', 'gul')
80
+ ->fields('gul', ['type', 'object_id'])
81
+ ->condition('id', $lastBuild, '>')
82
+ ->condition('id', $currentBuild, '<=')
83
+ ->condition('server', $server)
84
+ ->distinct()
85
+ ->execute()
86
+ ->fetchAll());
87
+ }
88
+
89
+ /**
90
+ * {@inheritDoc}}
91
+ */
92
+ public function latestBuild(string $server) : int {
93
+ $query = $this->database->select('gatsby_update_log');
94
+ $query->condition('server', $server);
95
+ $query->addExpression('max(id)');
96
+ return $query->execute()->fetchField() ?: -1;
97
+ }
98
+ }
@@ -0,0 +1,59 @@
1
+ <?php
2
+
3
+ namespace Drupal\silverback_gatsby;
4
+
5
+ interface GatsbyUpdateTrackerInterface {
6
+
7
+ /**
8
+ * Clear the in-memory de-duplication cache.
9
+ *
10
+ * Mainly used for unit testing.
11
+ */
12
+ public function clear() : void;
13
+
14
+ /**
15
+ * Track an update for a given GraphQL object.
16
+ *
17
+ * @param string $server
18
+ * The GraphQL server ID this log is related to.
19
+ * @param string $type
20
+ * The GraphQL type that has changed.
21
+ * @param string $id
22
+ * The id of the changed stream entry.
23
+ *
24
+ * @return int
25
+ * The unique build id for this entry.
26
+ *
27
+ * @throws \Exception
28
+ */
29
+ public function track(string $server, string $type, string $id) : int;
30
+
31
+ /**
32
+ * Retrieve the changes that happened between the last and the current build.
33
+ *
34
+ * @param int $lastBuild
35
+ * The last successful build id as returned by `log`.
36
+ * @param int $currentBuild
37
+ * The current build id as returned by `log`.
38
+ * @param string $server
39
+ * The GraphQL server id.
40
+ *
41
+ * @return \Drupal\silverback_gatsby\GatsbyUpdate[]
42
+ * A list of [GraphQLType, ID] tuples that can be used by the source plugin
43
+ * to fetch changes
44
+ */
45
+ public function diff(int $lastBuild, int $currentBuild, string $server) : array;
46
+
47
+ /**
48
+ * Retrieve the id of the latest build for a given server..
49
+ *
50
+ * Used for comparing states between systems.
51
+ *
52
+ * @param string $server
53
+ * The server id to retrieve the latest build for.
54
+ *
55
+ * @return int
56
+ */
57
+ public function latestBuild(string $server) : int;
58
+ }
59
+