Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: NDC generation tool #7

Merged
merged 30 commits into from
Mar 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
6606096
wip: codegen
hgiasac Feb 4, 2024
0b589e0
Merge branch 'main' into feat/generator
hgiasac Feb 4, 2024
b35cfba
wip: codegen
hgiasac Feb 8, 2024
c6d5337
Support v0.1.0-rc.14 of the NDC spec
hgiasac Feb 8, 2024
7406645
merge main and fix conflicts
hgiasac Feb 17, 2024
ce1f9df
wip: codegen
hgiasac Feb 18, 2024
92e2d67
wip: codegen
hgiasac Feb 18, 2024
7cd537a
improve codegen
hgiasac Feb 19, 2024
581f25f
add README
hgiasac Feb 19, 2024
3db2c07
update README and Dockerfile
hgiasac Feb 20, 2024
69f224a
merge main and fix conflicts
hgiasac Feb 21, 2024
8fcb43b
add package manifests
hgiasac Feb 21, 2024
376260a
merge main and fix conflicts
hgiasac Feb 23, 2024
27158e8
add unit tests
hgiasac Feb 24, 2024
143fc68
improve unit tests
hgiasac Feb 24, 2024
9e4914f
move codegen to cmd/ndc-go-sdk
hgiasac Feb 25, 2024
cccfe7c
update docs
hgiasac Feb 25, 2024
30a1b9d
improve tests
hgiasac Feb 25, 2024
6367f35
Merge branch 'main' into feat/generator
hgiasac Feb 25, 2024
9a39fad
add build scripts
hgiasac Feb 28, 2024
55316e4
add more utils
hgiasac Mar 2, 2024
295013b
add get value utils
hgiasac Mar 2, 2024
024174c
improve encode and decode methods
hgiasac Mar 3, 2024
11ffcd8
revert prometheus package
hgiasac Mar 3, 2024
4ac4222
revert otel package
hgiasac Mar 3, 2024
e9a6633
improve generation and tests
hgiasac Mar 3, 2024
f1604a8
rename utils
hgiasac Mar 3, 2024
da03e14
upgrade to v1.19
hgiasac Mar 3, 2024
3c080cc
upgrade to v1.20
hgiasac Mar 3, 2024
dfdab8f
remove unused function
hgiasac Mar 3, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ go.work
go.work.sum

.idea/
_output/
coverage.out
24 changes: 23 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
VERSION ?= $(shell ./scripts/get-CalVer.sh)
PLUGINS_BRANCH ?= master
OUTPUT_DIR := _output

.PHONY: typegen
typegen:
cd typegen && ./regenerate-schema.sh
Expand All @@ -14,4 +18,22 @@ test:
# https://golangci-lint.run/usage/install
.PHONY: lint
lint:
golangci-lint run
golangci-lint run

# clean the output directory
.PHONY: clean
clean:
rm -rf "$(OUTPUT_DIR)"

.PHONY: build-codegen
build-codegen:
go build -o _output/ndc-go-sdk ./cmd/ndc-go-sdk

# build the build-codegen cli for all given platform/arch
.PHONY: build-codegen
ci-build-codegen: export CGO_ENABLED=0
ci-build-codegen: clean
go run github.com/mitchellh/gox -ldflags '-X github.com/hasura/ndc-sdk-go/cmd/ndc-go-sdk/version.BuildVersion=$(VERSION) -s -w -extldflags "-static"' \
-osarch="linux/amd64 darwin/amd64 windows/amd64 darwin/arm64" \
-output="$(OUTPUT_DIR)/$(VERSION)/ndc-go-sdk-{{.OS}}-{{.Arch}}" \
./cmd/ndc-go-sdk
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ This SDK is mostly analogous to the Rust SDK, except where necessary.

All functions of the Connector interface are analogous to their Rust counterparts.

## Components
## Features

- Connector HTTP server
- Authentication
- Observability with OpenTelemetry and Prometheus

## Quick start

Checkout the [generation tool](cmd/ndc-go-sdk) to quickly setup and develop data connectors.

## Using this SDK

The SDK exports a `Start` function, which takes a `connector` object, that is an object that implements the `Connector` interface defined in [connector/types.go](connector/types.go)
Expand Down
213 changes: 213 additions & 0 deletions cmd/ndc-go-sdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
# Native Data Connector code generator

The NDC code generator provides a set of tools to develop data connectors quickly. It's suitable for developers who create connectors for business logic functions (or action in GraphQL Engine v2).

The generator is inspired by [ndc-typescript-deno](https://github.com/hasura/ndc-typescript-deno) and [ndc-nodejs-lambda](https://github.com/hasura/ndc-nodejs-lambda) that automatically infer TypeScript functions as NDC functions/procedures for use at runtime. It's possible to do this with Go via reflection. However, code generation is better for performance, type-safe, and no magic.

## Installation

- **Install from source**: To install with Go 1.19+:

```bash
go install github.com/hasura/ndc-sdk-go/cmd/ndc-go-sdk@latest
```

## How to Use

```bash
❯ ndc-go-sdk -h
Usage: ndc-go-sdk <command>

Flags:
-h, --help Show context-sensitive help.

Commands:
init --name=STRING --module=STRING
Initialize an NDC connector boilerplate. For example:

ndc-go-sdk init -n example -m github.com/foo/example

generate
Generate schema and implementation for the connector from functions.
```

### Initialize connector project

The `init` command generates a boilerplate project for connector development from [template](templates/new) with the following folder structure:

- `functions`: the folder contains query and mutation functions. The `generate` command will parse `.go` files in this folder.
- `types`: the folder contains reusable types such as `RawConfiguration`, `Configuration` and `State`.
- `connector.go`: parts of Connector methods, except `GetSchema`, `Query` and `Mutation` methods that will be generated by the `generate` command.
- `main.go`: the main function that runs the connector CLI.
- `go.mod`: the module file with required dependencies.
- `README.md`: the index README file.
- `Dockerfile`: the build template for Docker image.

The command requires names of connector and module. By default, the tool creates a new folder with the connector name. If you want to customize the path, or generate files in the current folder. use `--output` (`-o`) argument.

```bash
ndc-go-sdk init -n example -m github.com/foo/example -o .
```

### Generate queries and mutations

The `generate` command parses code in the `functions` folder, finds functions and types that are allowed to expose and generates following files:

- `schema.generated.json`: the generated connector schema in JSON format.
- `connector.generated.go`: implement `GetSchema`, `Query` and `Mutation` methods with exposed functions.

```bash
ndc-go-sdk generate
```

## How it works

### Functions

Functions which are allowed to expose as queries or mutations need to have `Function` or `Procedure` prefix in name. For example:

```go
// FunctionHello sends a hello message
func FunctionHello(ctx context.Context, state *types.State, arguments *HelloArguments) (*HelloResult, error)

// ProcedureCreateAuthor creates an author
func ProcedureCreateAuthor(ctx context.Context, state *types.State, arguments *CreateAuthorArguments) (*CreateAuthorResult, error)
```

Or use `@function` or `@procedure` comment tag:

```go
// Hello sends a hello message
// @function
func Hello(ctx context.Context, state *types.State, arguments *HelloArguments) (*HelloResult, error)

// CreateAuthor creates an author
// @procedure
func CreateAuthor(ctx context.Context, state *types.State, arguments *CreateAuthorArguments) (*CreateAuthorResult, error)

// you also can set the alias after the tag:

// Foo a bar
// @function bar
func Foo(ctx context.Context, state *types.State) (*FooResult, error)
```

Function and Procedure names will be formatted to `camelCase` by default.

> The generator detects comments by the nearby code position. It isn't perfectly accurate in some use cases. Prefix name in function is highly recommended.

A function must have 2 (no argument) or 3 parameters. `Context` and `State` are always present as 2 first parameters. The result is a tuple with a expected output and `error`.

> [Function](https://hasura.github.io/ndc-spec/specification/schema/functions.html) is a type of Query and [Procedure](https://hasura.github.io/ndc-spec/specification/schema/procedures.html) is a type of mutation. [Collection](https://hasura.github.io/ndc-spec/specification/schema/collections.html) is usually used for database queries so it isn't used for business logic.

### Types

The tool only infers arguments and result types of exposed functions to generate object type schemas:

- Argument type must be a struct with serializable properties.
- Result type can be a scalar, slice or struct.

#### Object Types

The tool can infer properties of the struct and generate [Object Type](https://hasura.github.io/ndc-spec/specification/schema/object-types.html) schema. The `json` tags will be read as properties name to be consistent with `JSON Marshaller` and `Unmarshaller`. For example, with the following type:

```go
type CreateAuthorResult struct {
ID int `json:"id"`
Name string `json:"name"`
}

// auto generated
// func (j CreateAuthorResult) ToMap() map[string]any {
// return map[string]any{
// "id": j.ID,
// "name": j.Name,
// }
// }
```

the schema will be:

```json
{
"CreateAuthorResult": {
"fields": {
"id": {
"type": {
"name": "Int",
"type": "named"
}
},
"name": {
"type": {
"name": "String",
"type": "named"
}
}
}
}
}
```

#### Scalar Types

**Supported types**

The basic scalar types supported are:

- `string` (NDC scalar type: `String`)
- `int`, `int8`, `int16`, `int32`, `int64`, `uint`, `uint8`, `uint16`, `uint32`, `uint64` (NDC scalar type: `Int`)
- `float32`, `float64` (NDC scalar type: `Float`)
- `complex64`, `complex128` (NDC scalar type: `Complex`)
- `bool` (NDC scalar type: `Boolean`)
- `time.Time` (NDC scalar type: `DateTime`, represented as an [ISO formatted](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) string in JSON)
- `time.Duration` (NDC scalar type: `Duration`, represented as a duration string in JSON)
- `github.com/google/uuid.UUID` (NDC scalar type: `UUID`, represented as an UUID string in JSON)

Alias scalar types will be inferred to the origin type in schema.

```go
// the scalar type in schema is still a `String`.
type Text string
```

If you want to define a custom scalar type, the type name must have a `Scalar` prefix or `@scalar` tag the comment. The generator doesn't care about the underlying type even it is a struct.

```go
type ScalarFoo struct {
Bar string
}
// output: Foo
// auto generated
// func (j ScalarFoo) ScalarName() string {
// return "Foo"
// }

// @scalar
type Tag struct {
tag string
}
// output: Tag


// @scalar Bar
type Foo struct {}
// output: Bar
```

> The generator detects comments by the nearby position. It isn't perfectly accurate in some use cases. Prefix name in function is highly recommended.

### Documentation

The tool parses comments of functions and types by the nearby code position to description properties in the schema. For example:

```go
// Creates an author
func ProcedureCreateAuthor(ctx context.Context, state *types.State, arguments *CreateAuthorArguments) (*CreateAuthorResult, error)

// {
// "name": "createAuthor",
// "description": "Creates an author",
// ...
// }
```
Loading
Loading