@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.
- package/CHANGELOG.md +11 -0
- package/drupal/silverback_gatsby/.prettierignore +1 -0
- package/drupal/silverback_gatsby/CHANGELOG.md +1216 -0
- package/drupal/silverback_gatsby/README.md +297 -0
- package/drupal/silverback_gatsby/composer.json +15 -0
- package/drupal/silverback_gatsby/directives.gql +63 -0
- package/drupal/silverback_gatsby/directives.graphql +62 -0
- package/drupal/silverback_gatsby/drush.services.yml +9 -0
- package/drupal/silverback_gatsby/graphql/entity.directive.graphqls +2 -0
- package/drupal/silverback_gatsby/graphql/menu.directive.graphqls +17 -0
- package/drupal/silverback_gatsby/graphql/silverback_gatsby.base.graphqls +25 -0
- package/drupal/silverback_gatsby/graphql/silverback_gatsby.extension.graphqls +10 -0
- package/drupal/silverback_gatsby/graphql/stringTranslation.directive.graphqls +4 -0
- package/drupal/silverback_gatsby/graphql/translatableString.directive.graphqls +2 -0
- package/drupal/silverback_gatsby/modules/silverback_gatsby_example/directives.gql +6 -0
- package/drupal/silverback_gatsby/modules/silverback_gatsby_example/graphql/.graphqlrc.json +4 -0
- package/drupal/silverback_gatsby/modules/silverback_gatsby_example/graphql/silverback_gatsby_example.graphqls +39 -0
- package/drupal/silverback_gatsby/modules/silverback_gatsby_example/silverback_gatsby_example.info.yml +7 -0
- package/drupal/silverback_gatsby/modules/silverback_gatsby_example/silverback_gatsby_example.module +16 -0
- package/drupal/silverback_gatsby/modules/silverback_gatsby_example/src/Directives.php +24 -0
- package/drupal/silverback_gatsby/silverback_gatsby.info.yml +8 -0
- package/drupal/silverback_gatsby/silverback_gatsby.install +180 -0
- package/drupal/silverback_gatsby/silverback_gatsby.links.task.yml +6 -0
- package/drupal/silverback_gatsby/silverback_gatsby.module +119 -0
- package/drupal/silverback_gatsby/silverback_gatsby.permissions.yml +8 -0
- package/drupal/silverback_gatsby/silverback_gatsby.post_update.php +15 -0
- package/drupal/silverback_gatsby/silverback_gatsby.routing.yml +28 -0
- package/drupal/silverback_gatsby/silverback_gatsby.services.yml +50 -0
- package/drupal/silverback_gatsby/src/Annotation/GatsbyFeed.php +41 -0
- package/drupal/silverback_gatsby/src/Commands/SilverbackGatsbyCommands.php +102 -0
- package/drupal/silverback_gatsby/src/Controller/BuildController.php +45 -0
- package/drupal/silverback_gatsby/src/Controller/PublisherController.php +34 -0
- package/drupal/silverback_gatsby/src/Directives.php +51 -0
- package/drupal/silverback_gatsby/src/GatsbyBuildTrigger.php +230 -0
- package/drupal/silverback_gatsby/src/GatsbyBuildTriggerInterface.php +52 -0
- package/drupal/silverback_gatsby/src/GatsbyUpdate.php +38 -0
- package/drupal/silverback_gatsby/src/GatsbyUpdateHandler.php +181 -0
- package/drupal/silverback_gatsby/src/GatsbyUpdateTracker.php +98 -0
- package/drupal/silverback_gatsby/src/GatsbyUpdateTrackerInterface.php +59 -0
- package/drupal/silverback_gatsby/src/GatsbyUpdateTrigger.php +69 -0
- package/drupal/silverback_gatsby/src/GatsbyUpdateTriggerInterface.php +22 -0
- package/drupal/silverback_gatsby/src/GraphQL/Build.php +235 -0
- package/drupal/silverback_gatsby/src/GraphQL/ComposableSchema.php +73 -0
- package/drupal/silverback_gatsby/src/LocaleStorageDecorator.php +181 -0
- package/drupal/silverback_gatsby/src/MenuTreeStorageDecorator.php +139 -0
- package/drupal/silverback_gatsby/src/Plugin/FeedBase.php +121 -0
- package/drupal/silverback_gatsby/src/Plugin/FeedInterface.php +132 -0
- package/drupal/silverback_gatsby/src/Plugin/FeedPluginManager.php +39 -0
- package/drupal/silverback_gatsby/src/Plugin/Gatsby/Feed/EntityFeed.php +252 -0
- package/drupal/silverback_gatsby/src/Plugin/Gatsby/Feed/MenuFeed.php +292 -0
- package/drupal/silverback_gatsby/src/Plugin/Gatsby/Feed/StringTranslationFeed.php +171 -0
- package/drupal/silverback_gatsby/src/Plugin/Gatsby/Feed/TranslatableStringFeed.php +128 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/CurrentUserEntity.php +43 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/EntityQueryBase.php +40 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/FetchEntity.php +371 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/FetchString.php +64 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/FetchTranslatableString.php +74 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/FocalPoint.php +47 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/GatsbyBuildId.php +36 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/GatsbyExtractId.php +28 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/GatsbyExtractLangcode.php +28 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/ImageProps.php +149 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/ListEntities.php +88 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/ListStrings.php +87 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/StringId.php +37 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/DataProducer/StringTranslations.php +94 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/Directive/EntityFetch.php +51 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/Directive/EntityTranslationsWithDefault.php +30 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/Directive/FocalPoint.php +27 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/Directive/ImageProps.php +27 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/Directive/MenuLangcode.php +27 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/Directive/MenuTranslations.php +36 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/Directive/SilverbackGatsbyEntityId.php +39 -0
- package/drupal/silverback_gatsby/src/Plugin/GraphQL/SchemaExtension/SilverbackGatsbySchemaExtension.php +384 -0
- package/drupal/silverback_gatsby/src/SilverbackGatsbyServiceProvider.php +43 -0
- package/drupal/silverback_gatsby/src/SilverbackGatsbySessionConfiguration.php +30 -0
- package/drupal/silverback_gatsby/src/SilverbackReverseProxyMiddleware.php +49 -0
- package/drupal/silverback_gatsby/tests/queries/create-page-fields.gql +12 -0
- package/drupal/silverback_gatsby/tests/queries/current-user.gql +6 -0
- package/drupal/silverback_gatsby/tests/queries/feed_info.gql +12 -0
- package/drupal/silverback_gatsby/tests/queries/load-entity.gql +5 -0
- package/drupal/silverback_gatsby/tests/queries/menus.gql +31 -0
- package/drupal/silverback_gatsby/tests/queries/multilingual-menus.gql +11 -0
- package/drupal/silverback_gatsby/tests/queries/revisionable-translatable.gql +15 -0
- package/drupal/silverback_gatsby/tests/queries/revisionable.gql +8 -0
- package/drupal/silverback_gatsby/tests/queries/translatable.gql +14 -0
- package/drupal/silverback_gatsby/tests/queries/untranslatable.gql +10 -0
- package/drupal/silverback_gatsby/tests/schema/.graphqlrc.json +4 -0
- package/drupal/silverback_gatsby/tests/schema/translatable-strings.graphql +7 -0
- package/drupal/silverback_gatsby/tests/src/Kernel/CurrentUserTest.php +37 -0
- package/drupal/silverback_gatsby/tests/src/Kernel/EntityFeedTest.php +490 -0
- package/drupal/silverback_gatsby/tests/src/Kernel/EntityFeedTestBase.php +178 -0
- package/drupal/silverback_gatsby/tests/src/Kernel/GatsbyBuildTriggerTest.php +155 -0
- package/drupal/silverback_gatsby/tests/src/Kernel/GatsbyFeedInfoTest.php +319 -0
- package/drupal/silverback_gatsby/tests/src/Kernel/GatsbyUpdateHandlerTest.php +131 -0
- package/drupal/silverback_gatsby/tests/src/Kernel/GatsbyUpdateTrackerTest.php +196 -0
- package/drupal/silverback_gatsby/tests/src/Kernel/GatsbyUpdateTriggerTest.php +177 -0
- package/drupal/silverback_gatsby/tests/src/Kernel/MenuFeedTest.php +192 -0
- package/drupal/silverback_gatsby/tests/src/Kernel/TranslatableStringFeedTest.php +210 -0
- package/drupal/silverback_gatsby/tests/src/Traits/NotificationCheckTrait.php +43 -0
- 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
|
+
|