Interakcja z API GraphQL
Interakcja z API GraphQLUruchamianie Gato GraphQL bez WordPress

Uruchamianie Gato GraphQL bez WordPress

Gato GraphQL został zbudowany przy użyciu samodzielnych komponentów PHP, zarządzanych przez Composer, w taki sposób, że wszystkie komponenty PHP tworzące serwer GraphQL nie zależą od WordPress!

Dzięki temu serwer GraphQL może działać jako samodzielna aplikacja PHP, którą możesz włączyć do dowolnej aplikacji PHP — opartej na WordPress lub na czymkolwiek innym.

Jeśli dla danego przypadku użycia Twoja aplikacja nie potrzebuje dostępu do danych WordPress, to przynajmniej dla tego przypadku użycia jesteś gotowy do działania.

Poniższy film demonstruje taki przypadek użycia: interakcję z API GitHuba w celu pobierania/instalowania artefaktów z GitHub Actions podczas developmentu:

Demo headless WordPress bez WordPress: wykonywanie query GraphQL

W filmie query GraphQL wykonuje żądanie HTTP, aby pobrać najnowsze wtyczki Gato GraphQL wygenerowane w GitHub Actions, które są przesyłane jako artefakty przy scalaniu pull requesta.

Adresy URL artefaktów z odpowiedzi GraphQL są następnie wstrzykiwane do WP-CLI, aby wtyczki były automatycznie instalowane na lokalnym serwerze DEV, w celu uruchomienia testów.

W tym przypadku użycia, ponieważ żadne dane WordPress nie są w ogóle dostępne, serwer GraphQL może już działać jako samodzielna aplikacja PHP.

Szczegółowo: Uruchamianie Gato GraphQL jako samodzielnej aplikacji PHP

Poniżej znajduje się szczegółowe wyjaśnienie demonstracyjnego filmu.

Dostarczamy query GraphQL do uruchomienia w pliku retrieve-github-artifacts.gql.

Query łączy się z API GitHuba, pobierając token dostępu ze zmiennej środowiskowej GITHUB_ACCESS_TOKEN. Dynamicznie generuje pełną ścieżkę do endpointu actions/artifacts na podstawie podanych zmiennych, a następnie wysyła do niego żądanie HTTP.

Z odpowiedzi wyodrębnia "download URL" z każdego elementu artefaktu i wysyła asynchroniczne żądania HTTP pod te adresy. Z nagłówka Location każdego z tych "download URL" uzyskujemy rzeczywisty adres URL pobieranego pliku.

Na koniec wypisuje wszystkie adresy URL razem, oddzielone spacją, aby ułatwić wstrzyknięcie do WP-CLI.

# File retrieve-github-artifacts.gql
 
query RetrieveProxyArtifactDownloadURLs(
  $repoOwner: String!
  $repoProject: String!
  $perPage: Int = 1
  $artifactName: String = ""
) {
  githubAccessToken: _env(name: "GITHUB_ACCESS_TOKEN")
    @remove
 
  # Tworzy nagłówek autoryzacji do wysłania do GitHub
  authorizationHeader: _sprintf(
    string: "Bearer %s"
    values: [$__githubAccessToken]
  )
    @remove
 
  # Tworzy nagłówek autoryzacji do wysłania do GitHub
  githubRequestHeaders: _echo(
    value: [
      { name: "Accept", value: "application/vnd.github+json" }
      { name: "Authorization", value: $__authorizationHeader }
    ]
  )
    @remove
    @export(as: "githubRequestHeaders")
 
  githubAPIEndpoint: _sprintf(
    string: "https://api.github.com/repos/%s/%s/actions/artifacts?per_page=%s&name=%s"
    values: [$repoOwner, $repoProject, $perPage, $artifactName]
  )
 
  # Używa pola z "Send HTTP Request Fields" do połączenia z GitHub
  gitHubArtifactData: _sendJSONObjectItemHTTPRequest(
    input: {
      url: $__githubAPIEndpoint
      options: { headers: $__githubRequestHeaders }
    }
  )
    @remove
 
  # Na koniec wyodrębnia URL z każdego elementu "artifacts"
  gitHubProxyArtifactDownloadURLs: _objectProperty(
    object: $__gitHubArtifactData
    by: { key: "artifacts" }
  )
    @underEachArrayItem(passValueOnwardsAs: "artifactItem")
      @applyField(
        name: "_objectProperty"
        arguments: { object: $artifactItem, by: { key: "archive_download_url" } }
        setResultInResponse: true
      )
    @export(as: "gitHubProxyArtifactDownloadURLs")
}
 
query CreateHTTPRequestInputs
  @depends(on: "RetrieveProxyArtifactDownloadURLs")
{
  httpRequestInputs: _echo(value: $gitHubProxyArtifactDownloadURLs)
    @underEachArrayItem(passValueOnwardsAs: "url")
      @applyField(
        name: "_objectAddEntry"
        arguments: {
          object: {
            options: { headers: $githubRequestHeaders, allowRedirects: null }
          }
          key: "url"
          value: $url
        }
        setResultInResponse: true
      )
    @export(as: "httpRequestInputs")
    @remove
}
 
query RetrieveActualArtifactDownloadURLs
  @depends(on: "CreateHTTPRequestInputs")
{
  _sendHTTPRequests(inputs: $httpRequestInputs) {
    artifactDownloadURL: header(name: "Location")
      @export(as: "artifactDownloadURLs", type: LIST)
  }
}
 
query PrintSpaceSeparatedArtifactDownloadURLs
  @depends(on: "RetrieveActualArtifactDownloadURLs")
{
  spaceSeparatedArtifactDownloadURLs: _arrayJoin(
    array: $artifactDownloadURLs
    separator: " "
  )
}

Logika PHP bezpośrednio ładuje kod z wtyczki Gato GraphQL oraz z pakietu "Power Extensions" (wymaganego do wysyłania żądań HTTP i innych funkcjonalności).

Jako samodzielna aplikacja PHP musimy jawnie wskazać, które moduły są inicjalizowane, oraz podać wszelką niestandardową konfigurację.

Na przykład, informujemy moduł SendHTTPRequests, aby zezwolił na łączenie się z https://api.github.com/repos, oraz moduł EnvironmentFields, aby zezwolił na dostęp do zmiennej środowiskowej GITHUB_ACCESS_TOKEN.

Należy zauważyć, że schemat GraphQL jest generowany przy pierwszym wykonaniu query GraphQL i zapisywany w pamięci podręcznej na dysku. Dzięki temu od 2. razu wzwyż żaden kod obliczający schemat nie jest wykonywany, co przyspiesza działanie.

Na koniec samodzielna aplikacja inicjalizuje serwer GraphQL, wykonuje query względem niego i wypisuje odpowiedź.

<?php
// File retrieve-github-artifacts.php
 
declare(strict_types=1);
 
use GraphQLByPoP\GraphQLServer\Server\StandaloneGraphQLServer;
use PoP\Root\Container\ContainerCacheConfiguration;
 
// Ładuje serwer GraphQL przez samodzielne komponenty PHP
require_once (__DIR__ . '/wordpress/wp-content/plugins/gatographql/vendor/scoper-autoload.php');
 
// Ładuje rozszerzenia PRO przez samodzielne komponenty PHP
require_once (__DIR__ . '/wordpress/wp-content/plugins/gatographql-power-extensions-bundle/vendor/scoper-autoload.php');
 
// Moduły wymagane w query GraphQL
$moduleClasses = [
  \PoPSchema\EnvironmentFields\Module::class,
  \PoPSchema\FunctionFields\Module::class,
  \GraphQLByPoP\ExportDirective\Module::class,
  \GraphQLByPoP\DependsOnOperationsDirective\Module::class,
  \GraphQLByPoP\RemoveDirective\Module::class,
  \PoPSchema\ApplyFieldDirective\Module::class,
  \PoPSchema\SendHTTPRequests\Module::class,
  \PoPSchema\ConditionalMetaDirectives\Module::class,
  \PoPSchema\DataIterationMetaDirectives\Module::class,
];
 
// Konfiguruje moduły
$moduleClassConfiguration = [
  \PoP\GraphQLParser\Module::class => [
    \PoP\GraphQLParser\Environment::ENABLE_MULTIPLE_QUERY_EXECUTION => true,
    \PoP\GraphQLParser\Environment::USE_LAST_OPERATION_IN_DOCUMENT_FOR_MULTIPLE_QUERY_EXECUTION_WHEN_OPERATION_NAME_NOT_PROVIDED => true,
    \PoP\GraphQLParser\Environment::ENABLE_RESOLVED_FIELD_VARIABLE_REFERENCES => true,
    \PoP\GraphQLParser\Environment::ENABLE_COMPOSABLE_DIRECTIVES => true,
  ],
  \PoPSchema\SendHTTPRequests\Module::class => [
    \PoPSchema\SendHTTPRequests\Environment::SEND_HTTP_REQUEST_URL_ENTRIES => [
      '#https://api.github.com/repos/(.*)#',
    ],
  ],
  \PoPSchema\EnvironmentFields\Module::class => [
    \PoPSchema\EnvironmentFields\Environment::ENVIRONMENT_VARIABLE_OR_PHP_CONSTANT_ENTRIES => [
      'GITHUB_ACCESS_TOKEN',
    ],
  ],
];
 
// Zapisuje schemat w pamięci podręcznej na dysku, aby przyspieszyć wykonanie od 2. razu
$containerCacheConfiguration = new ContainerCacheConfiguration('MyGraphQLServer', true, 'retrieve-github-artifacts', __DIR__ . '/tmp');
 
// Inicjalizuje serwer
$graphQLServer = new StandaloneGraphQLServer($moduleClasses, $moduleClassConfiguration, [], [], $containerCacheConfiguration);
 
/**
 * Query GraphQL do wykonania, zapisane we własnym pliku .gql
 *
 * @var string
 */
$query = file_get_contents(__DIR__ . '/retrieve-github-artifacts.gql');
 
// Zmienne GraphQL
$variables = [
  'repoOwner' => 'GatoGraphQL',
  'repoProject' => 'GatoGraphQL',
  'perPage' => 3
];
 
// Wykonuje query
$response = $graphQLServer->execute(
  $query,
  $variables,
);
 
// Wypisuje odpowiedź
echo $response->getContent();

Aby wykonać query GraphQL, uruchamiamy w terminalu (używając jq do czytelnego wydruku JSON):

php retrieve-github-artifacts.php | jq

Na koniec, aby wyodrębnić adresy URL artefaktów z odpowiedzi GraphQL i wstrzyknąć je do WP-CLI, uruchamiamy:

GITHUB_ARTIFACT_URLS=$(php retrieve-github-artifacts.php \
  | grep -E -o '"spaceSeparatedArtifactDownloadURLs\":"(.*)"' \
  | cut -d':' -f2- | cut -d'"' -f2- | rev | cut -d'"' -f2- | rev \
  | sed 's/\\\//\//g')
wp plugin install ${GITHUB_ARTIFACT_URLS} --force --activate