From 62ea1243b7af094bc0dcef055d2dd2ff13f320c9 Mon Sep 17 00:00:00 2001 From: Tristen Harr Date: Wed, 28 Aug 2024 18:01:12 -0500 Subject: [PATCH] update README --- README.md | 115 ++++++++++++++++++++++++++++++++------------------ docs/logo.png | Bin 0 -> 12965 bytes 2 files changed, 73 insertions(+), 42 deletions(-) create mode 100644 docs/logo.png diff --git a/README.md b/README.md index 1795c24..066125d 100644 --- a/README.md +++ b/README.md @@ -1,71 +1,102 @@ -# hasura-ndc-neo4j +# Hasura Neo4j Connector + -### To start the server: +The Hasura Neo4j Connector allows for connecting to a Neo4j database to give you an instant GraphQL API on top of your Neo4j data. -All commands will require some environment variables to connect to your Neo4j instance, for example: -NEO4J_URL=neo4j://localhost:7687/neo4j -NEO4J_USER= -NEO4J_PASS= +This connector is built using the [Typescript Data Connector SDK](https://github.com/hasura/ndc-sdk-typescript) and implements the [Data Connector Spec](https://github.com/hasura/ndc-spec). -Remember to provide them when running the following commands. +- [See the listing in the Hasura Hub](https://hasura.io/connectors/neo4j) +- [Hasura V3 Documentation](https://hasura.io/docs/3.0/index/) -#### Prerequisite: Set-up configuration file +## Features -Use the included CLI tool by running `npm run update:config`. -This will introspect your database and create the corresponding configuration file in the root directory, named `configuration.json`. The connector will use this file in running mode. +Below, you'll find a matrix of all supported features for the Turso connector: -2. Run `npm install` +| Feature | Supported | Notes | +| ------------------------------- | --------- | ----- | +| Native Queries + Logical Models | ❌ | | +| Simple Object Query | ✅ | | +| Filter / Search | ✅ | | +| Simple Aggregation | ❌ | | +| Sort | ✅ | | +| Paginate | ✅ | | +| Table Relationships | ✅ | | +| Views | ❌ | | +| Distinct | ❌ | | +| Remote Relationships | ✅ | | +| Custom Fields | ❌ | | +| Mutations | ❌ | | -3. Run `npm start` +## Before you get Started -### To run the Hasura tests: +1. The [DDN CLI](https://hasura.io/docs/3.0/cli/installation) and [Docker](https://docs.docker.com/engine/install/) installed +2. A [supergraph](https://hasura.io/docs/3.0/getting-started/init-supergraph) +3. A [subgraph](https://hasura.io/docs/3.0/getting-started/init-subgraph) +4. Have a [Neo4j](https://neo4j.com/product/neo4j-graph-database/) database, along with login credentials. -1. Make sure the server is started by following the instructions above. +The steps below explain how to Initialize and configure a connector for local development. You can learn how to deploy a +connector — after it's been configured — [here](https://hasura.io/docs/3.0/getting-started/deployment/deploy-a-connector). -2. Run `cargo run --bin ndc-test -- test --endpoint http://localhost:8100` from the `ndc-spec`. More info can be found [here](https://github.com/hasura/ndc-spec/tree/main#test-an-agent). +## Using the Neo4j connector -### To test the features of the connector by sending different requests and asserting the responses: +### Step 1: Authenticate your CLI session -1. Make sure the server is started in test mode. For this, follow the instructions above but run the `npm run start:test` command as the last step. +```bash +ddn auth login +``` -2. Run `npm run test` +### Step 2: Configure the connector -3. Any new tests you want to run should be inside the `__tests__/requests` folder and should follow the schema described in the `__tests__/data/configuration.json` file +Once you have an initialized supergraph and subgraph, run the initialization command in interactive mode while providing a name for the connector in the prompt: -## Development +```bash +ddn connector init neo4j -i +``` -Prerequisite: Steps 1-3 from https://hasura.io/docs/3.0/local-dev/#step-1-prerequisites +#### Step 2.1: Choose the `neo4j/neo4j` option from the list -1. Start server locally +#### Step 2.2: Choose a port for the connector -2. Make sure the connector URL is your local server URL +The CLI will ask for a specific port to run the connector on. Choose a port that is not already in use or use the default suggested port. -```yml -definition: - name: neo4j_connector - url: - singleUrl: - value: http://localhost: -``` +#### Step 2.3: Provide the env var(s) for the connector -## Deploy connector +| Name | Description | +|-|-| +| NEO4J_URL | The connection string for the Neo4j database | +| NEO4J_USER | The username for the Neo4J database | +| NEO4J_PASS | The password for the Neo4J database | -1. Start server locally +You'll find the environment variables in the `.env` file and they will be in the format: -2. Use Hasura extension to refresh connector, track collections, track relationships +`__` -- only track Array relationships -- rename relationships if necessary +Here is an example of what your `.env` file might look like: -3. Start Hasura daemon: `hasura3 daemon start` +``` +APP_NEO4J_AUTHORIZATION_HEADER="Bearer vrHKneV3KIs-qz5dbIbFsg==" +APP_NEO4J_HASURA_SERVICE_TOKEN_SECRET="vrHKneV3KIs-qz5dbIbFsg==" +APP_NEO4J_NEO4J_PASS="2j..." +APP_NEO4J_NEO4J_URL="neo4j+s://47b154c4.databases.neo4j.io" +APP_NEO4J_NEO4J_USER="neo4j" +APP_NEO4J_OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="http://local.hasura.dev:4317" +APP_NEO4J_OTEL_SERVICE_NAME="app_neo4j" +APP_NEO4J_READ_URL="http://local.hasura.dev:8781" +APP_NEO4J_WRITE_URL="http://local.hasura.dev:8781" +``` + +### Step 3: Introspect the connector -4. Get Tunnel Endpoint +Introspecting the connector will generate a `configuration.json` file and a `neo4j.hml` file. -- check if exists: `hasura3 tunnel list` -- or create one: `hasura3 tunnel create localhost:` +```bash +ddn connector introspect neo4j +``` -5. Change connector URL to Tunnel Endpoint +### Step 4: Add your resources -6. Deploy connector: `hasura3 build create` +You can add the models, commands, and relationships to your API by tracking them which generates the HML files. -7. Run queries in Hasura Console using the latest build: https://console.hasura.io/project/grown-pegasus-6631 +```bash +ddn connector-link add-resources neo4j +``` \ No newline at end of file diff --git a/docs/logo.png b/docs/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..fabf6ce36045016e4296c9e468e06dd6bf260bb0 GIT binary patch literal 12965 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