Şema eğitimi
Şema eğitimiDers 30: İçeriği bir upstream siteden birden fazla downstream siteye dağıtma

Ders 30: İçeriği bir upstream siteden birden fazla downstream siteye dağıtma

Diyelim ki bir medya şirketinin farklı bölgeler için bir WordPress siteleri ağı var ve her haber makalesi yalnızca o bölge için uygunsa bir siteye yayınlanıyor.

Bu durum için aşağıdaki mimariyi uygulamak mantıklıdır:

  • Tüm içerik, içerik için tek gerçek kaynağı olarak görev yapan tek bir upstream WordPress sitesine yayınlanır (ve yalnızca orada düzenlenir)
  • Uygun içerik, her bölgesel downstream WordPress sitesine dağıtılır (ancak bu sitelerde düzenlenmez)

Bu tutorial dersi, söz konusu mimarinin nasıl uygulanacağını gösterecektir: upstream WordPress sitesinin ilgili Gato GraphQL uzantılarının aktif olması gerekir; downstream sitelerin ise yalnızca ücretsiz Gato GraphQL eklentisine sahip olması yeterlidir.

İçeriği upstream'den downstream sitelere senkronize etmek için GraphQL query

(Yalnızca downstream siteler için) Bu GraphQL query'nin çalışması için, endpoint'e uygulanan Schema Configuration içinde Nested Mutations etkinleştirilmiş olmalıdır

Aşağıdaki GraphQL query, güncellenen gönderinin içeriğini ilgili downstream sitelere senkronize etmek amacıyla upstream WordPress sitesinde çalıştırılır; siteler arasında ortak tanımlayıcı olarak gönderi slug'ı kullanılır.

(Query, önceki tutorial dersinde açıklandığı üzere diğer özellikleri — etiketler, kategoriler, yazar ve öne çıkan görsel — de senkronize edecek şekilde uyarlanabilir.)

Query, işlemsel mantık içerir; böylece herhangi bir downstream sitede güncelleme başarısız olduğunda — ister HTTP isteği başarısız olsun (sunucu kapalıyken olduğu gibi) ister GraphQL query hata üretsin (sağlanan slug ile eşleşen gönderi yoksa olduğu gibi) — mutation tüm downstream sitelerde geri alınır.

Durumu geri almak için $previousPostContent değişkeninin sağlanması gerekir. Bu değeri, GraphQL query'nin çalıştırıldığı post_updated WordPress action'ına kanca takarak aktarabiliriz (önceki bir tutorial dersinde açıklandığı gibi).

Query şunları yapar:

  • Güncellenen gönderinin slug'ını ve yeni ile önceki içeriğini alır
  • Gönderiden "downstream_domains" meta özelliğini alır; bu özellik, gönderinin dağıtılması gereken downstream sitelerin alan adlarını içeren bir dizi barındırır
  • Meta özellik mevcut değilse (yani değeri null ise), wp_options tablosundan "downstream_domains" seçeneğini alır; bu seçenek tüm downstream alan adlarının listesini içerir
  • Kullanıcıyı her bir downstream siteye giriş yaptırır (basitlik adına aynı $username ve $userPassword kullanılarak) ve gönderi içeriğini güncellemek için mutation'ı çalıştırır
  • Herhangi bir downstream site hata üretirse, mutation tüm downstream sitelerde geri alınır
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})
    @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 ExecuteAll
  @depends(on: "RevertGraphQLHTTPRequests")
{
  id @remove
}

Yukarıdaki GraphQL query'de, bir gönderinin "downstream_domains" meta özelliği boş bir dizi olarak tanımlandığında, gönderi hiçbir downstream siteye dağıtılmaz.

Bu durum, _notNull ve _notEmpty fonksiyon alanları arasındaki fark sayesinde mümkündür (PHP Functions via Schema uzantısı tarafından sağlanır):

  • "downstream_domains" meta özelliği tanımlı değilse değeri null olur ve hem _notNull hem de _notEmpty false döndürür
  • "downstream_domains" meta özelliği boş bir dizi olarak tanımlanmışsa değeri [] olur ve yalnızca _notEmpty false döndürür