

As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.

# Usar o protocolo Bolt para fazer consultas do openCypher ao Neptune
<a name="access-graph-opencypher-bolt"></a>

[O [Bolt](https://boltprotocol.org/) é um client/server protocolo orientado a declarações desenvolvido inicialmente pela Neo4j e licenciado sob a licença Creative Commons 3.0 Attribution-. ShareAlike](https://creativecommons.org/licenses/by-sa/3.0/) É orientado por cliente, o que significa que o cliente sempre inicia as trocas de mensagens.

Para se conectar ao Neptune usando os drivers Bolt da Neo4j, basta substituir o URL e o número da porta pelos endpoints de cluster usando o esquema URI `bolt`. Se você tiver uma única instância do Neptune em execução, use o endpoint read\$1write. Caso várias instâncias estejam em execução, dois drivers são recomendados, um para o gravador e outro para todas as réplicas de leitura. Caso você tenha apenas dois endpoints padrão, um driver read\$1write e um read\$1only são suficientes, mas se também tiver endpoints personalizados, pense em criar uma instância de driver para cada um.

**nota**  
Embora a especificação do Bolt afirme que o Bolt pode se conectar usando TCP ou WebSockets, o Neptune suporta apenas conexões TCP para o Bolt.

O Neptune permite até 1.000 conexões Bolt simultâneas em todos os tamanhos de instância, exceto para t3.medium e t4g.medium. Nas instâncias t3.medium e t4g.medium, somente 512 conexões são permitidas.

Para obter exemplos de consulta do openCypher em várias linguagens que usem os drivers Bolt, consulte a documentação [Drivers & Language Guides](https://neo4j.com/developer/language-guides/) da Neo4j.

**Importante**  
Os drivers Neo4j Bolt para Python JavaScript, .NET e Golang inicialmente não suportavam a renovação automática dos tokens de autenticação Signature v4. AWS Isso significa que, após a expiração da assinatura (geralmente em cinco minutos), o driver não foi autenticado e ocorreu uma falha nas solicitações subsequentes. Os exemplos de Python JavaScript, .NET e Go abaixo foram todos afetados por esse problema.  
[Consulte o [problema \$1834 do driver Neo4j Python, o problema](https://github.com/neo4j/neo4j-python-driver/issues/834)[\$1664 do Neo4j .NET, o problema](https://github.com/neo4j/neo4j-dotnet-driver/issues/664) \$1993 do driver Neo4j e o [problema \$1429 do JavaScript driver Neo4j](https://github.com/neo4j/neo4j-javascript-driver/issues/993) GoLang para obter mais informações.](https://github.com/neo4j/neo4j-go-driver/issues/429)  
A partir da versão 5.8.0 do driver, uma nova versão prévia da API de reautenticação foi lançada para o driver Go (consulte [v5.8.0 - Feedback wanted on re-authentication](https://github.com/neo4j/neo4j-go-driver/discussions/482)).

## Usar o Bolt com Java para conectar-se ao Neptune
<a name="access-graph-opencypher-bolt-java"></a>

É possível baixar um driver para qualquer versão que quiser usar do [MVN Repository](https://mvnrepository.com/artifact/org.neo4j.driver/neo4j-java-driver) do Maven ou adicionar essa dependência ao projeto:

```
<dependency>
  <groupId>org.neo4j.driver</groupId>
  <artifactId>neo4j-java-driver</artifactId>
  <version>4.3.3</version>
</dependency>
```

Em seguida, para se conectar ao Neptune em Java usando um desses drivers Bolt, crie uma instância de driver para a instância em seu cluster usando um código como primary/writer o seguinte:

```
import org.neo4j.driver.Driver;
import org.neo4j.driver.GraphDatabase;

final Driver driver =
  GraphDatabase.driver("bolt://(your cluster endpoint URL):(your cluster port)",
    AuthTokens.none(),
    Config.builder().withEncryption()
                    .withTrustStrategy(TrustStrategy.trustSystemCertificates())
                    .build());
```

Se você tiver uma ou mais réplicas de leitor, também poderá criar uma instância de driver para elas usando um código como este:

```
final Driver read_only_driver =              // (without connection timeout)
  GraphDatabase.driver("bolt://(your cluster endpoint URL):(your cluster port)",
      Config.builder().withEncryption()
                      .withTrustStrategy(TrustStrategy.trustSystemCertificates())
                      .build());
```

Ou com um tempo limite:

```
final Driver read_only_timeout_driver =      // (with connection timeout)
  GraphDatabase.driver("bolt://(your cluster endpoint URL):(your cluster port)",
    Config.builder().withConnectionTimeout(30, TimeUnit.SECONDS)
                    .withEncryption()
                    .withTrustStrategy(TrustStrategy.trustSystemCertificates())
                    .build());
```

Se você tiver endpoints personalizados, também poderá valer a pena criar uma instância de driver para cada um.

## Um exemplo de consulta do openCypher em Python usando Bolt
<a name="access-graph-opencypher-bolt-python"></a>

Veja como fazer uma consulta do openCypher em Python usando o Bolt:

```
python -m pip install neo4j
```

```
from neo4j import GraphDatabase
uri = "bolt://(your cluster endpoint URL):(your cluster port)"
driver = GraphDatabase.driver(uri, auth=("username", "password"), encrypted=True)
```

Observe que os parâmetros `auth` são ignorados.

## Um exemplo de consulta do openCypher em .NET usando o Bolt
<a name="access-graph-opencypher-bolt-dotnet"></a>

Para fazer uma consulta OpenCypher no.NET usando o Bolt, a primeira etapa é instalar o driver Neo4j usando. NuHet Para fazer chamadas síncronas, use a versão `.Simple`, desta forma:

```
Install-Package Neo4j.Driver.Simple-4.3.0
```

```
using Neo4j.Driver;

namespace hello
{
  // This example creates a node and reads a node in a Neptune
  // Cluster where IAM Authentication is not enabled.
  public class HelloWorldExample : IDisposable
  {
    private bool _disposed = false;
    private readonly IDriver _driver;
    private static string url = "bolt://(your cluster endpoint URL):(your cluster port)";
    private static string createNodeQuery = "CREATE (a:Greeting) SET a.message = 'HelloWorldExample'";
    private static string readNodeQuery = "MATCH(n:Greeting) RETURN n.message";

    ~HelloWorldExample() => Dispose(false);

    public HelloWorldExample(string uri)
    {
      _driver = GraphDatabase.Driver(uri, AuthTokens.None, o => o.WithEncryptionLevel(EncryptionLevel.Encrypted));
    }

    public void createNode()
    {
      // Open a session
      using (var session = _driver.Session())
      {
         // Run the query in a write transaction
        var greeting = session.WriteTransaction(tx =>
        {
          var result = tx.Run(createNodeQuery);
          // Consume the result
          return result.Consume();
        });

        // The output will look like this:
        //   ResultSummary{Query=`CREATE (a:Greeting) SET a.message = 'HelloWorldExample".....
        Console.WriteLine(greeting);
      }
    }

    public void retrieveNode()
    {
      // Open a session
      using (var session = _driver.Session())
      {
        // Run the query in a read transaction
        var greeting = session.ReadTransaction(tx =>
        {
          var result = tx.Run(readNodeQuery);
          // Consume the result. Read the single node
          // created in a previous step.
          return result.Single()[0].As<string>();
        });
        // The output will look like this:
        //   HelloWorldExample
        Console.WriteLine(greeting);
      }
    }

    public void Dispose()
    {
      Dispose(true);
      GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
      if (_disposed)
        return;
      if (disposing)
      {
        _driver?.Dispose();
      }
      _disposed = true;
    }

    public static void Main()
    {
      using (var apiCaller = new HelloWorldExample(url))
      {
        apiCaller.createNode();
        apiCaller.retrieveNode();
      }
    }
  }
}
```

## Um exemplo de consulta do openCypher em Java usando o Bolt com autenticação do IAM
<a name="access-graph-opencypher-bolt-java-iam-auth"></a>

O código Java abaixo mostra como fazer consultas do openCypher em Java usando o Bolt com autenticação do IAM. O JavaDoc comentário descreve seu uso. Quando uma instância de driver estiver disponível, você poderá usá-la para fazer várias solicitações autenticadas.

```
package software.amazon.neptune.bolt;

import com.amazonaws.DefaultRequest;
import com.amazonaws.Request;
import com.amazonaws.auth.AWS4Signer;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.http.HttpMethodName;
import com.google.gson.Gson;
import lombok.Builder;
import lombok.Getter;
import lombok.NonNull;
import org.neo4j.driver.Value;
import org.neo4j.driver.Values;
import org.neo4j.driver.internal.security.InternalAuthToken;
import org.neo4j.driver.internal.value.StringValue;

import java.net.URI;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import static com.amazonaws.auth.internal.SignerConstants.AUTHORIZATION;
import static com.amazonaws.auth.internal.SignerConstants.HOST;
import static com.amazonaws.auth.internal.SignerConstants.X_AMZ_DATE;
import static com.amazonaws.auth.internal.SignerConstants.X_AMZ_SECURITY_TOKEN;

/**
 * Use this class instead of `AuthTokens.basic` when working with an IAM
 * auth-enabled server. It works the same as `AuthTokens.basic` when using
 * static credentials, and avoids making requests with an expired signature
 * when using temporary credentials. Internally, it generates a new signature
 * on every invocation (this may change in a future implementation).
 *
 * Note that authentication happens only the first time for a pooled connection.
 *
 * Typical usage:
 *
 * NeptuneAuthToken authToken = NeptuneAuthToken.builder()
 *     .credentialsProvider(credentialsProvider)
 *     .region("aws region")
 *     .url("cluster endpoint url")
 *     .build();
 *
 * Driver driver = GraphDatabase.driver(
 *     authToken.getUrl(),
 *     authToken,
 *     config
 * );
 */

public class NeptuneAuthToken extends InternalAuthToken {
  private static final String SCHEME = "basic";
  private static final String REALM = "realm";
  private static final String SERVICE_NAME = "neptune-db";
  private static final String HTTP_METHOD_HDR = "HttpMethod";
  private static final String DUMMY_USERNAME = "username";
  @NonNull
  private final String region;
  @NonNull
  @Getter
  private final String url;
  @NonNull
  private final AWSCredentialsProvider credentialsProvider;
  private final Gson gson = new Gson();

  @Builder
  private NeptuneAuthToken(
      @NonNull final String region,
      @NonNull final String url,
      @NonNull final AWSCredentialsProvider credentialsProvider
  ) {
      // The superclass caches the result of toMap(), which we don't want
      super(Collections.emptyMap());
      this.region = region;
      this.url = url;
      this.credentialsProvider = credentialsProvider;
  }

  @Override
  public Map<String, Value> toMap() {
    final Map<String, Value> map = new HashMap<>();
    map.put(SCHEME_KEY, Values.value(SCHEME));
    map.put(PRINCIPAL_KEY, Values.value(DUMMY_USERNAME));
    map.put(CREDENTIALS_KEY, new StringValue(getSignedHeader()));
    map.put(REALM_KEY, Values.value(REALM));

    return map;
  }

  private String getSignedHeader() {
    final Request<Void> request = new DefaultRequest<>(SERVICE_NAME);
    request.setHttpMethod(HttpMethodName.GET);
    request.setEndpoint(URI.create(url));
    // Comment out the following line if you're using an engine version older than 1.2.0.0
    request.setResourcePath("/opencypher");

    final AWS4Signer signer = new AWS4Signer();
    signer.setRegionName(region);
    signer.setServiceName(request.getServiceName());
    signer.sign(request, credentialsProvider.getCredentials());

    return getAuthInfoJson(request);
  }

  private String getAuthInfoJson(final Request<Void> request) {
    final Map<String, Object> obj = new HashMap<>();
    obj.put(AUTHORIZATION, request.getHeaders().get(AUTHORIZATION));
    obj.put(HTTP_METHOD_HDR, request.getHttpMethod());
    obj.put(X_AMZ_DATE, request.getHeaders().get(X_AMZ_DATE));
    obj.put(HOST, request.getHeaders().get(HOST));
    obj.put(X_AMZ_SECURITY_TOKEN, request.getHeaders().get(X_AMZ_SECURITY_TOKEN));

    return gson.toJson(obj);
  }
}
```

## Um exemplo de consulta do openCypher em Python usando o Bolt com autenticação do IAM
<a name="access-graph-opencypher-bolt-python-iam-auth"></a>

A classe Python abaixo permite que você faça consultas do openCypher em Python usando o Bolt com autenticação do IAM:

```
import json

from neo4j import Auth
from botocore.awsrequest import AWSRequest
from botocore.credentials import Credentials
from botocore.auth import (
  SigV4Auth,
  _host_from_url,
)

SCHEME = "basic"
REALM = "realm"
SERVICE_NAME = "neptune-db"
DUMMY_USERNAME = "username"
HTTP_METHOD_HDR = "HttpMethod"
HTTP_METHOD = "GET"
AUTHORIZATION = "Authorization"
X_AMZ_DATE = "X-Amz-Date"
X_AMZ_SECURITY_TOKEN = "X-Amz-Security-Token"
HOST = "Host"


class NeptuneAuthToken(Auth):
  def __init__(
    self,
    credentials: Credentials,
    region: str,
    url: str,
    **parameters
  ):
    # Do NOT add "/opencypher" in the line below if you're using an engine version older than 1.2.0.0
    request = AWSRequest(method=HTTP_METHOD, url=url + "/opencypher")
    request.headers.add_header("Host", _host_from_url(request.url))
    sigv4 = SigV4Auth(credentials, SERVICE_NAME, region)
    sigv4.add_auth(request)

    auth_obj = {
      hdr: request.headers[hdr]
      for hdr in [AUTHORIZATION, X_AMZ_DATE, X_AMZ_SECURITY_TOKEN, HOST]
    }
    auth_obj[HTTP_METHOD_HDR] = request.method
    creds: str = json.dumps(auth_obj)
    super().__init__(SCHEME, DUMMY_USERNAME, creds, REALM, **parameters)
```

Você usa essa classe para criar um driver da seguinte forma:

```
  authToken = NeptuneAuthToken(creds, REGION, URL)
  driver = GraphDatabase.driver(URL, auth=authToken, encrypted=True)
```

## Um exemplo de Node.js usando a autenticação do IAM e o Bolt
<a name="access-graph-opencypher-bolt-nodejs-iam-auth"></a>

O código Node.js abaixo usa o AWS SDK da JavaScript versão 3 e a ES6 sintaxe para criar um driver que autentica as solicitações:

```
import neo4j from "neo4j-driver";
import { HttpRequest }  from "@smithy/protocol-http";
import { defaultProvider } from "@aws-sdk/credential-provider-node";
import { SignatureV4 } from "@smithy/signature-v4";
import crypto from "@aws-crypto/sha256-js";
const { Sha256 } = crypto;
import assert from "node:assert";

const region = "us-west-2";
const serviceName = "neptune-db";
const host = "(your cluster endpoint URL)";
const port = 8182;
const protocol = "bolt";
const hostPort = host + ":" + port;
const url = protocol + "://" + hostPort;
const createQuery = "CREATE (n:Greeting {message: 'Hello'}) RETURN ID(n)";
const readQuery = "MATCH(n:Greeting) WHERE ID(n) = $id RETURN n.message";

async function signedHeader() {
  const req = new HttpRequest({
    method: "GET",
    protocol: protocol,
    hostname: host,
    port: port,
    // Comment out the following line if you're using an engine version older than 1.2.0.0
    path: "/opencypher",
    headers: {
      host: hostPort
    }
  });

  const signer = new SignatureV4({
    credentials: defaultProvider(),
    region: region,
    service: serviceName,
    sha256: Sha256
  });

  return signer.sign(req, { unsignableHeaders: new Set(["x-amz-content-sha256"]) })
    .then((signedRequest) => {
      const authInfo = {
        "Authorization": signedRequest.headers["authorization"],
        "HttpMethod": signedRequest.method,
        "X-Amz-Date": signedRequest.headers["x-amz-date"],
        "Host": signedRequest.headers["host"],
        "X-Amz-Security-Token": signedRequest.headers["x-amz-security-token"]
      };
      return JSON.stringify(authInfo);
    });
}

async function createDriver() {
  let authToken = { scheme: "basic", realm: "realm", principal: "username", credentials: await signedHeader() };

  return neo4j.driver(url, authToken, {
      encrypted: "ENCRYPTION_ON",
      trust: "TRUST_SYSTEM_CA_SIGNED_CERTIFICATES",
      maxConnectionPoolSize: 1,
      // logging: neo4j.logging.console("debug")
    }
  );
}

async function unmanagedTxn(driver) {
  const session = driver.session();
  const tx = session.beginTransaction();
  try {
    const created = await tx.run(createQuery);
    const matched = await tx.run(readQuery, { id: created.records[0].get(0) });
    const msg = matched.records[0].get("n.message");
    assert.equal(msg, "Hello");
    await tx.commit();
  } catch (err) {
    // The transaction will be rolled back, now handle the error.
    console.log(err);
  } finally {
    await session.close();
  }
}

const driver = await createDriver();
try {
  await unmanagedTxn(driver);
} catch (err) {
  console.log(err);
} finally {
  await driver.close();
}
```

## Um exemplo de consulta do openCypher em .NET usando o Bolt com autenticação do IAM
<a name="access-graph-opencypher-bolt-dotnet-iam-auth"></a>

Para habilitar a autenticação do IAM em .NET, é necessário assinar uma solicitação ao estabelecer a conexão. O exemplo abaixo mostra como criar um auxiliar `NeptuneAuthToken` para gerar um token de autenticação:

```
using Amazon.Runtime;
using Amazon.Util;
using Neo4j.Driver;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Web;

namespace Hello
{
  /*
   * Use this class instead of `AuthTokens.None` when working with an IAM-auth-enabled server.
   *
   * Note that authentication happens only the first time for a pooled connection.
   *
   * Typical usage:
   *
   * var authToken = new NeptuneAuthToken(AccessKey, SecretKey, Region).GetAuthToken(Host);
   * _driver = GraphDatabase.Driver(Url, authToken, o => o.WithEncryptionLevel(EncryptionLevel.Encrypted));
   */

  public class NeptuneAuthToken
  {
    private const string ServiceName = "neptune-db";
    private const string Scheme = "basic";
    private const string Realm = "realm";
    private const string DummyUserName = "username";
    private const string Algorithm = "AWS4-HMAC-SHA256";
    private const string AWSRequest = "aws4_request";

    private readonly string _accessKey;
    private readonly string _secretKey;
    private readonly string _region;

    private readonly string _emptyPayloadHash;

    private readonly SHA256 _sha256;


    public NeptuneAuthToken(string awsKey = null, string secretKey = null, string region = null)
    {
      var awsCredentials = awsKey == null || secretKey == null
        ? FallbackCredentialsFactory.GetCredentials().GetCredentials()
        : null;

      _accessKey = awsKey ?? awsCredentials.AccessKey;
      _secretKey = secretKey ?? awsCredentials.SecretKey;
      _region = region ?? FallbackRegionFactory.GetRegionEndpoint().SystemName; //ex: us-east-1

      _sha256 = SHA256.Create();
      _emptyPayloadHash = Hash(Array.Empty<byte>());
    }

    public IAuthToken GetAuthToken(string url)
    {
      return AuthTokens.Custom(DummyUserName, GetCredentials(url), Realm, Scheme);
    }

    /******************** AWS SIGNING FUNCTIONS *********************/
    private string Hash(byte[] bytesToHash)
    {
      return ToHexString(_sha256.ComputeHash(bytesToHash));
    }

    private static byte[] HmacSHA256(byte[] key, string data)
    {
      return new HMACSHA256(key).ComputeHash(Encoding.UTF8.GetBytes(data));
    }

    private byte[] GetSignatureKey(string dateStamp)
    {
      var kSecret = Encoding.UTF8.GetBytes($"AWS4{_secretKey}");
      var kDate = HmacSHA256(kSecret, dateStamp);
      var kRegion = HmacSHA256(kDate, _region);
      var kService = HmacSHA256(kRegion, ServiceName);
      return HmacSHA256(kService, AWSRequest);
    }

    private static string ToHexString(byte[] array)
    {
      return Convert.ToHexString(array).ToLowerInvariant();
    }

    private string GetCredentials(string url)
    {
      var request = new HttpRequestMessage
      {
        Method = HttpMethod.Get,
        RequestUri = new Uri($"https://{url}/opencypher")
      };

      var signedrequest = Sign(request);

      var headers = new Dictionary<string, object>
      {
        [HeaderKeys.AuthorizationHeader] = signedrequest.Headers.GetValues(HeaderKeys.AuthorizationHeader).FirstOrDefault(),
        ["HttpMethod"] = HttpMethod.Get.ToString(),
        [HeaderKeys.XAmzDateHeader] = signedrequest.Headers.GetValues(HeaderKeys.XAmzDateHeader).FirstOrDefault(),
        // Host should be capitalized, not like in Amazon.Util.HeaderKeys.HostHeader
        ["Host"] = signedrequest.Headers.GetValues(HeaderKeys.HostHeader).FirstOrDefault(),
      };

      return JsonSerializer.Serialize(headers);
    }

    private HttpRequestMessage Sign(HttpRequestMessage request)
    {
      var now = DateTimeOffset.UtcNow;
      var amzdate = now.ToString("yyyyMMddTHHmmssZ");
      var datestamp = now.ToString("yyyyMMdd");

      if (request.Headers.Host == null)
      {
        request.Headers.Host = $"{request.RequestUri.Host}:{request.RequestUri.Port}";
      }

      request.Headers.Add(HeaderKeys.XAmzDateHeader, amzdate);

      var canonicalQueryParams = GetCanonicalQueryParams(request);

      var canonicalRequest = new StringBuilder();
      canonicalRequest.Append(request.Method + "\n");
      canonicalRequest.Append(request.RequestUri.AbsolutePath + "\n");
      canonicalRequest.Append(canonicalQueryParams + "\n");

      var signedHeadersList = new List<string>();
      foreach (var header in request.Headers.OrderBy(a => a.Key.ToLowerInvariant()))
      {
        canonicalRequest.Append(header.Key.ToLowerInvariant());
        canonicalRequest.Append(':');
        canonicalRequest.Append(string.Join(",", header.Value.Select(s => s.Trim())));
        canonicalRequest.Append('\n');
        signedHeadersList.Add(header.Key.ToLowerInvariant());
      }
      canonicalRequest.Append('\n');

      var signedHeaders = string.Join(";", signedHeadersList);
      canonicalRequest.Append(signedHeaders + "\n");
      canonicalRequest.Append(_emptyPayloadHash);

      var credentialScope = $"{datestamp}/{_region}/{ServiceName}/{AWSRequest}";
      var stringToSign = $"{Algorithm}\n{amzdate}\n{credentialScope}\n"
        + Hash(Encoding.UTF8.GetBytes(canonicalRequest.ToString()));

      var signing_key = GetSignatureKey(datestamp);
      var signature = ToHexString(HmacSHA256(signing_key, stringToSign));

      request.Headers.TryAddWithoutValidation(HeaderKeys.AuthorizationHeader,
        $"{Algorithm} Credential={_accessKey}/{credentialScope}, SignedHeaders={signedHeaders}, Signature={signature}");

      return request;
    }

    private static string GetCanonicalQueryParams(HttpRequestMessage request)
    {
      var querystring = HttpUtility.ParseQueryString(request.RequestUri.Query);

      // Query params must be escaped in upper case (i.e. "%2C", not "%2c").
      var queryParams = querystring.AllKeys.OrderBy(a => a)
        .Select(key => $"{key}={Uri.EscapeDataString(querystring[key])}");
      return string.Join("&", queryParams);
    }
  }
}
```

Veja como fazer uma consulta do openCypher em .NET usando o Bolt com autenticação do IAM. O exemplo abaixo usa o auxiliar `NeptuneAuthToken`:

```
using Neo4j.Driver;

namespace Hello
{
  public class HelloWorldExample
  {
    private const string Host = "(your hostname):8182";
    private const string Url = $"bolt://{Host}";
    private const string CreateNodeQuery = "CREATE (a:Greeting) SET a.message = 'HelloWorldExample'";
    private const string ReadNodeQuery = "MATCH(n:Greeting) RETURN n.message";

    private const string AccessKey = "(your access key)";
    private const string SecretKey = "(your secret key)";
    private const string Region = "(your AWS region)"; // e.g. "us-west-2"

    private readonly IDriver _driver;

    public HelloWorldExample()
    {
      var authToken = new NeptuneAuthToken(AccessKey, SecretKey, Region).GetAuthToken(Host);

      // Note that when the connection is reinitialized after max connection lifetime
      // has been reached, the signature token could have already been expired (usually 5 min)
      // You can face exceptions like:
      //   `Unexpected server exception 'Signature expired: XXXX is now earlier than YYYY (ZZZZ - 5 min.)`
      _driver = GraphDatabase.Driver(Url, authToken, o =>
                o.WithMaxConnectionLifetime(TimeSpan.FromMinutes(60)).WithEncryptionLevel(EncryptionLevel.Encrypted));
    }

    public async Task CreateNode()
    {
      // Open a session
      using (var session = _driver.AsyncSession())
      {
        // Run the query in a write transaction
        var greeting = await session.WriteTransactionAsync(async tx =>
        {
          var result = await tx.RunAsync(CreateNodeQuery);
          // Consume the result
          return await result.ConsumeAsync();
        });

        // The output will look like this:
        //   ResultSummary{Query=`CREATE (a:Greeting) SET a.message = 'HelloWorldExample".....
        Console.WriteLine(greeting.Query);
      }
    }

    public async Task RetrieveNode()
    {
      // Open a session
      using (var session = _driver.AsyncSession())
      {
        // Run the query in a read transaction
        var greeting = await session.ReadTransactionAsync(async tx =>
        {
          var result = await tx.RunAsync(ReadNodeQuery);
          var records = await result.ToListAsync();

          // Consume the result. Read the single node
          // created in a previous step.
          return records[0].Values.First().Value;
        });
        // The output will look like this:
        //   HelloWorldExample
        Console.WriteLine(greeting);
      }
    }
  }
}
```

Esse exemplo pode ser iniciado executando o código abaixo em `.NET 6` ou `.NET 7` com os seguintes pacotes:
+ **`Neo4j`**`.Driver=4.3.0`
+ **`AWSSDK`**`.Core=3.7.102.1`

```
namespace Hello
{
  class Program
  {
    static async Task Main()
    {
      var apiCaller = new HelloWorldExample();

      await apiCaller.CreateNode();
      await apiCaller.RetrieveNode();
    }
  }
}
```

## Um exemplo de consulta do openCypher em Golang usando o Bolt com autenticação do IAM
<a name="access-graph-opencypher-bolt-golang-iam-auth"></a>

O exemplo a seguir mostra como fazer consultas OpenCypher em Go usando o protocolo Bolt com autenticação IAM. Ele usa o AWS SDK for Go v2 para assinatura SigV4 e o driver Neo4j Go v5 com `AuthTokenManager` uma estrutura que implementa a interface do gerenciador de tokens do driver Neo4j Go () para atualizar automaticamente as credenciais antes que elas expirem. `github.com/neo4j/neo4j-go-driver/v5/neo4j/auth.TokenManager`

Primeiro, crie um `AuthTokenManager` que gere tokens assinados com SIGV4. Salve-a como `auth_token_manager.go`:

```
// AuthTokenManager for Amazon Neptune IAM authentication via the Bolt protocol.
// Provides SigV4-signed credentials to the Neo4j driver's auth interface.
package main

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"reflect"
	"sync"
	"time"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/aws/signer/v4"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/credentials/stscreds"
	"github.com/aws/aws-sdk-go-v2/service/sts"
	"github.com/neo4j/neo4j-go-driver/v5/neo4j"
	"github.com/neo4j/neo4j-go-driver/v5/neo4j/db"
)

const (
	serviceName      = "neptune-db"
	emptyPayloadHash = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
)

// AuthTokenManager manages SigV4-signed authentication tokens for Neptune with automatic refresh.
type AuthTokenManager struct {
	region          string
	endpoint        string
	refreshInterval time.Duration
	credentials     aws.CredentialsProvider
	mutex           sync.Mutex
	cachedToken     neo4j.AuthToken
	tokenTime       time.Time
}

// NewAuthTokenManager creates a new AuthTokenManager.
//
// Parameters:
//   - region: AWS region (e.g., "us-east-1")
//   - endpoint: Neptune endpoint with port (e.g., "cluster.region.neptune.amazonaws.com:8182")
//   - profile: AWS profile name (optional, pass "" to use default)
//   - roleArn: AWS role ARN to assume (optional, pass "" to skip)
//   - refreshInterval: Token refresh interval
func NewAuthTokenManager(region, endpoint, profile, roleArn string, refreshInterval time.Duration) (*AuthTokenManager, error) {
	credentials, err := createCredentialsProvider(region, profile, roleArn)
	if err != nil {
		return nil, err
	}
	return &AuthTokenManager{
		region:          region,
		endpoint:        endpoint,
		refreshInterval: refreshInterval,
		credentials:     credentials,
	}, nil
}

// createCredentialsProvider builds an AWS credentials provider, optionally using
// a named profile and/or assuming a role.
func createCredentialsProvider(region, profile, roleArn string) (aws.CredentialsProvider, error) {
	ctx := context.Background()
	var opts []func(*config.LoadOptions) error
	opts = append(opts, config.WithRegion(region))
	if profile != "" {
		opts = append(opts, config.WithSharedConfigProfile(profile))
	}
	cfg, err := config.LoadDefaultConfig(ctx, opts...)
	if err != nil {
		return nil, fmt.Errorf("failed to load AWS config: %w", err)
	}
	var credentials aws.CredentialsProvider = cfg.Credentials
	if roleArn != "" {
		stsClient := sts.NewFromConfig(cfg)
		credentials = stscreds.NewAssumeRoleProvider(stsClient, roleArn, func(o *stscreds.AssumeRoleOptions) {
			o.RoleSessionName = "NeptuneAuthSession"
			o.Duration = 900 * time.Second
		})
	}
	return credentials, nil
}

// GetAuthToken returns a valid authentication token, using cached token if still valid.
func (m *AuthTokenManager) GetAuthToken(ctx context.Context) (neo4j.AuthToken, error) {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	if time.Since(m.tokenTime) < m.refreshInterval && m.cachedToken.Tokens != nil {
		return m.cachedToken, nil
	}

	token, err := m.generateToken(ctx)
	if err != nil {
		return neo4j.AuthToken{}, err
	}

	m.cachedToken = token
	m.tokenTime = time.Now()
	return token, nil
}

// HandleSecurityException handles security exceptions by invalidating the cached
// token (if it matches the token that caused the error) and returning true so
// the driver retries with a fresh token. The comparison prevents a concurrent
// retry from unnecessarily invalidating a freshly generated token.
func (m *AuthTokenManager) HandleSecurityException(ctx context.Context, token neo4j.AuthToken, err *db.Neo4jError) (bool, error) {
	m.mutex.Lock()
	defer m.mutex.Unlock()
	if reflect.DeepEqual(m.cachedToken.Tokens, token.Tokens) {
		m.tokenTime = time.Time{}
		m.cachedToken = neo4j.AuthToken{}
	}
	return true, nil
}

// generateToken generates a new SigV4-signed authentication token for Neptune.
func (m *AuthTokenManager) generateToken(ctx context.Context) (neo4j.AuthToken, error) {
	req, err := http.NewRequest(http.MethodGet, "https://"+m.endpoint+"/opencypher", nil)
	if err != nil {
		return neo4j.AuthToken{}, err
	}
	req.Host = m.endpoint

	creds, err := m.credentials.Retrieve(ctx)
	if err != nil {
		return neo4j.AuthToken{}, err
	}

	signer := v4.NewSigner()
	err = signer.SignHTTP(ctx, creds, req, emptyPayloadHash, serviceName, m.region, time.Now())
	if err != nil {
		return neo4j.AuthToken{}, err
	}

	authData := map[string]string{
		"Authorization": req.Header.Get("Authorization"),
		"X-Amz-Date":    req.Header.Get("X-Amz-Date"),
		"Host":          m.endpoint,
		"HttpMethod":    req.Method,
	}

	if st := req.Header.Get("X-Amz-Security-Token"); st != "" {
		authData["X-Amz-Security-Token"] = st
	}

	authJSON, err := json.Marshal(authData)
	if err != nil {
		return neo4j.AuthToken{}, err
	}

	return neo4j.BasicAuth("username", string(authJSON), ""), nil
}
```

Em seguida, use o gerenciador de tokens para criar um driver e encontrar um nó por ID. Observe o uso de uma consulta parametrizada (`$nodeId`) em vez da interpolação de strings:

```
package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/neo4j/neo4j-go-driver/v5/neo4j"
)

func findNode(ctx context.Context, driver neo4j.DriverWithContext, nodeId string) (string, error) {
	session := driver.NewSession(ctx, neo4j.SessionConfig{
		AccessMode: neo4j.AccessModeRead,
	})
	defer session.Close(ctx)

	// Use parameterized queries to prevent injection and enable query plan caching.
	result, err := session.Run(ctx,
		"MATCH (n) WHERE ID(n) = $nodeId RETURN n",
		map[string]any{"nodeId": nodeId},
	)
	if err != nil {
		return "", fmt.Errorf("error running query: %v", err)
	}

	if !result.Next(ctx) {
		if err = result.Err(); err != nil {
			return "", fmt.Errorf("error fetching result: %v", err)
		}
		return "", fmt.Errorf("node not found")
	}

	n, found := result.Record().Get("n")
	if !found {
		return "", fmt.Errorf("node not found")
	}

	return fmt.Sprintf("%+v", n), nil
}

func main() {
	region := "(your AWS region)"                  // e.g. "us-east-1"
	endpoint := "(your Neptune endpoint):8182"     // e.g. "cluster.xxx.us-east-1.neptune.amazonaws.com:8182"

	ctx := context.Background()

	// Pass a profile name for local development, or a role ARN for cross-account access
	authManager, err := NewAuthTokenManager(region, endpoint, "", "", 4*time.Minute) // Refresh before the 5-minute SigV4 signature expiry
	if err != nil {
		log.Fatalf("auth manager error: %v", err)
	}

	// bolt+s:// enables TLS with full certificate verification.
	// If you're developing on macOS, use bolt+ssc:// instead. Go on macOS uses the
	// system TLS verifier, which requires Certificate Transparency compliance that
	// Neptune endpoints don't support. In production (typically Linux), bolt+s://
	// works with system CA certificates.
	driver, err := neo4j.NewDriverWithContext("bolt+s://"+endpoint, authManager)
	if err != nil {
		log.Fatalf("driver error: %v", err)
	}
	defer driver.Close(ctx)

	if err = driver.VerifyConnectivity(ctx); err != nil {
		log.Fatalf("connectivity error: %v", err)
	}

	// Neptune assigns UUID-format node IDs if a user does not supply their own node IDs
	res, err := findNode(ctx, driver, "72c2e8c1-7d5f-5f30-10ca-9d2bb8c4afbc")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(res)
}
```

**nota**  
**A `auth.TokenManager` interface (`github.com/neo4j/neo4j-go-driver/v5/neo4j/auth`) usada neste exemplo tornou-se geralmente disponível no driver Neo4j Go v5.14.0.** Essa interface permite a atualização automática de credenciais, que é necessária para a autenticação do Neptune IAM, pois as assinaturas SigV4 são válidas apenas por um curto período e devem ser regeneradas quando o driver estabelece novas conexões.

Esse exemplo foi validado usando os seguintes módulos Go:

```
require (
    github.com/aws/aws-sdk-go-v2         v1.30.3
    github.com/aws/aws-sdk-go-v2/config   v1.27.27
    github.com/aws/aws-sdk-go-v2/credentials v1.17.27
    github.com/aws/aws-sdk-go-v2/service/sts v1.30.3
    github.com/neo4j/neo4j-go-driver/v5   v5.22.0
  )
```

## Comportamento da conexão do Bolt no Neptune
<a name="access-graph-opencypher-bolt-connections"></a>

Veja alguns fatos sobre conexões do Bolt no Neptune:
+ Como as conexões do Bolt são criadas na camada TCP, você não pode usar um [Application Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html) na frente delas, como acontece com um endpoint HTTP.
+ A porta que o Neptune usa para conexões do Bolt é a porta do cluster de banco de dados.
+ Com base no preâmbulo do Bolt passado para ele, o servidor do Neptune seleciona a versão mais atualizada do Bolt (1, 2, 3 ou 4.0).
+ O número máximo de conexões com o servidor do Neptune que um cliente pode abrir em qualquer momento é mil.
+ Se o cliente não fechar uma conexão após uma consulta, essa conexão poderá ser usada para executar a próxima consulta.
+ No entanto, se uma conexão ficar inativa por vinte minutos, o servidor a fechará automaticamente.
+ Se a autenticação do IAM não estiver habilitada, você poderá usar `AuthTokens.none()` em vez de fornecer um nome de usuário e senha fictícios. Por exemplo, em Java:

  ```
  GraphDatabase.driver("bolt://(your cluster endpoint URL):(your cluster port)", AuthTokens.none(),
      Config.builder().withEncryption().withTrustStrategy(TrustStrategy.trustSystemCertificates()).build());
  ```
+ Quando a autenticação do IAM é habilitada, uma conexão do Bolt é sempre desconectada alguns minutos mais de dez dias após ter sido estabelecida, caso ainda não tenha sido fechada por algum outro motivo.
+ Se o cliente enviar uma consulta para execução em uma conexão sem ter consumido os resultados de uma consulta anterior, a nova consulta será descartada. Em vez disso, para descartar os resultados anteriores, o cliente deve enviar uma mensagem de redefinição pela conexão.
+ Somente uma transação pode ser criada por vez em uma conexão específica.
+ Se ocorrer uma exceção durante uma transação, o servidor do Neptune reverterá a transação e fechará a conexão. Nesse caso, o driver cria uma conexão para a próxima consulta.
+ Esteja ciente de que as sessões não são seguras para threads. Várias operações paralelas devem usar várias sessões separadas.