Distribuer le contenu d'un site upstream vers plusieurs sites WordPress downstream
Imaginons qu'une entreprise de médias dispose d'un réseau de sites WordPress pour différentes régions, et que chaque article d'actualité est publié sur un site ou non, uniquement s'il convient à cette région.
Dans cette situation, il est judicieux de mettre en œuvre une architecture où :
- Tout le contenu est publié (et modifié) sur un seul site WordPress upstream, qui fait office de source de vérité unique pour le contenu
- Le contenu approprié est distribué (mais non modifié) sur chacun des sites WordPress régionaux downstream
Cette requête satisfait cette architecture : le site WordPress upstream doit avoir les extensions Gato GraphQL pertinentes activées, tandis que les sites downstream n'ont besoin que du plugin gratuit Gato GraphQL.
La requête est exécutée sur le site WordPress upstream pour synchroniser le contenu de l'article mis à jour avec les sites downstream pertinents, en utilisant le slug de l'article comme identifiant commun entre les sites.
Elle inclut une logique transactionnelle, de sorte que si la mise à jour échoue sur un site downstream — que ce soit parce que la requête HTTP a échoué (par exemple lorsque le serveur est hors ligne) ou parce que la requête GraphQL a produit des erreurs (par exemple s'il n'existe aucun article avec le slug fourni) — la mutation est alors annulée sur tous les sites downstream.
Notez que les domaines downstream sont fournis via la propriété meta "downstream_domains" de l'article, qui contient un tableau avec les domaines des sites downstream vers lesquels l'article doit être distribué. Si cette propriété meta n'existe pas, l'option "downstream_domains" est alors récupérée dans la table wp_options, qui contient la liste de tous les domaines downstream.
La requête connecte ensuite l'utilisateur à chacun des sites downstream (en utilisant les mêmes $username et $userPassword) et exécute la mutation pour mettre à jour le contenu de l'article. Si un site downstream produit une erreur, la mutation est annulée sur tous les sites downstream.
Cette requête nécessite que l'endpoint ait les Mutations imbriquées activées.
query InitializeDynamicVariables
@configureWarningsOnExportingDuplicateVariable(enabled: false)
{
initVariablesWithFalse: _echo(value: false)
@export(as: "requestProducedErrors")
@export(as: "anyErrorProduced")
@export(as: "hasDownstreamDomains")
@remove
}
query GetCustomDownstreamDomains($postSlug: String!)
@depends(on: "InitializeDynamicVariables")
{
post(by: { slug: $postSlug }, status: any)
@fail(
message: "There is no post in the upstream site with the provided slug"
data: {
slug: $postSlug
}
)
{
customDownstreamDomains: metaValues(key: "downstream_domains")
@export(as: "downstreamDomains")
hasDefinedCustomDownstreamDomains: _notNull(value: $__customDownstreamDomains)
@export(as: "hasDefinedCustomDownstreamDomains")
@remove
hasCustomDownstreamDomains: _notEmpty(value: $__customDownstreamDomains)
@export(as: "hasDownstreamDomains")
}
isMissingPostInUpstream: _isNull(value: $__post)
@export(as: "isMissingPostInUpstream")
}
query GetAllDownstreamDomains
@depends(on: "GetCustomDownstreamDomains")
@skip(if: $isMissingPostInUpstream)
@skip(if: $hasDefinedCustomDownstreamDomains)
{
allDownstreamDomains: optionValues(name: "downstream_domains")
@export(as: "downstreamDomains")
hasAllDownstreamDomains: _notEmpty(value: $__allDownstreamDomains)
@export(as: "hasDownstreamDomains")
}
############################################################
# (By default) Append "/graphql" to the domain, to point
# to that site's GraphQL single endpoint
############################################################
query ExportDownstreamGraphQLEndpointsAndQuery(
$endpointPath: String! = "/graphql"
)
@depends(on: "GetAllDownstreamDomains")
@skip(if: $isMissingPostInUpstream)
@include(if: $hasDownstreamDomains)
{
downstreamGraphQLEndpoints: _echo(value: $downstreamDomains)
@underEachArrayItem(
passValueOnwardsAs: "domain"
)
@strAppend(string: $endpointPath)
@export(as: "downstreamGraphQLEndpoints")
query: _echo(value: """
mutation LoginUserAndUpdatePost(
$username: String!
$userPassword: String!
$postSlug: String!
$postContent: String!
) {
loginUser(by: {
credentials: {
usernameOrEmail: $username,
password: $userPassword
}
}) {
userID
}
post(by: { slug: $postSlug }, status: any)
@fail(
message: "There is no post in the downstream site with the provided slug"
data: {
slug: $postSlug
}
)
{
update(input: {
contentAs: { html: $postContent },
}) {
status
errors {
__typename
...on ErrorPayload {
message
}
}
post {
slug
rawContent
}
}
}
}
"""
)
@export(as: "query")
@remove
}
query ExportSendGraphQLHTTPRequestInputs(
$username: String!
$userPassword: String!
$postSlug: String!
$newPostContent: String!
)
@depends(on: "ExportDownstreamGraphQLEndpointsAndQuery")
@skip(if: $isMissingPostInUpstream)
@include(if: $hasDownstreamDomains)
{
sendGraphQLHTTPRequestInputs: _echo(value: $downstreamGraphQLEndpoints)
@underEachArrayItem(
passValueOnwardsAs: "endpoint"
)
@applyField(
name: "_echo",
arguments: {
value: {
endpoint: $endpoint,
query: $query,
variables: [
{
name: "username",
value: $username
},
{
name: "userPassword",
value: $userPassword
},
{
name: "postSlug",
value: $postSlug
},
{
name: "postContent",
value: $newPostContent
}
]
}
},
setResultInResponse: true
)
@export(as: "sendGraphQLHTTPRequestInputs")
@remove
}
query SendGraphQLHTTPRequests
@depends(on: "ExportSendGraphQLHTTPRequestInputs")
@skip(if: $isMissingPostInUpstream)
@include(if: $hasDownstreamDomains)
{
downstreamGraphQLResponses: _sendGraphQLHTTPRequests(
inputs: $sendGraphQLHTTPRequestInputs
)
@export(as: "downstreamGraphQLResponses")
requestProducedErrors: _isNull(value: $__downstreamGraphQLResponses)
@export(as: "requestProducedErrors")
@export(as: "anyErrorProduced")
@remove
}
query ExportGraphQLResponsesHaveErrors
@depends(on: "SendGraphQLHTTPRequests")
@skip(if: $isMissingPostInUpstream)
@skip(if: $requestProducedErrors)
@include(if: $hasDownstreamDomains)
{
graphQLResponsesHaveErrors: _echo(value: $downstreamGraphQLResponses)
# Check if any GraphQL response has the "errors" entry
@underEachArrayItem(
passValueOnwardsAs: "response"
affectDirectivesUnderPos: [1, 2]
)
@applyField(
name: "_propertyIsSetInJSONObject"
arguments: {
object: $response
by: {
key: "errors"
}
}
setResultInResponse: true
)
@export(as: "graphQLResponsesHaveErrors")
@remove
}
query ValidateGraphQLResponsesHaveErrors
@depends(on: "ExportGraphQLResponsesHaveErrors")
@skip(if: $isMissingPostInUpstream)
@skip(if: $requestProducedErrors)
@include(if: $hasDownstreamDomains)
{
anyGraphQLResponseHasErrors: _or(values: $graphQLResponsesHaveErrors)
@export(as: "anyErrorProduced")
@remove
}
query ExportRevertGraphQLHTTPRequestInputs(
$username: String!
$userPassword: String!
$postSlug: String!
$previousPostContent: String!
)
@depends(on: "ValidateGraphQLResponsesHaveErrors")
@include(if: $hasDownstreamDomains)
@include(if: $anyErrorProduced)
{
revertGraphQLHTTPRequestInputs: _echo(value: $downstreamGraphQLEndpoints)
@underEachArrayItem(
passValueOnwardsAs: "endpoint"
)
@applyField(
name: "_echo",
arguments: {
value: {
endpoint: $endpoint,
query: $query,
variables: [
{
name: "username",
value: $username
},
{
name: "userPassword",
value: $userPassword
},
{
name: "postSlug",
value: $postSlug
},
{
name: "postContent",
value: $previousPostContent
}
]
}
},
setResultInResponse: true
)
@export(as: "revertGraphQLHTTPRequestInputs")
@remove
}
query RevertGraphQLHTTPRequests
@depends(on: "ExportRevertGraphQLHTTPRequestInputs")
@skip(if: $isMissingPostInUpstream)
@include(if: $hasDownstreamDomains)
@include(if: $anyErrorProduced)
{
revertGraphQLResponses: _sendGraphQLHTTPRequests(
inputs: $sendGraphQLHTTPRequestInputs
)
}
query DistributeContentFromUpstreamToMultipleDownstreamWPSites
@depends(on: "RevertGraphQLHTTPRequests")
{
id @remove
}