Skip to content

Commit

Permalink
update functions template and SDK
Browse files Browse the repository at this point in the history
  • Loading branch information
TristenHarr committed Jul 18, 2024
1 parent 4678b62 commit 5324c43
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 58 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ This changelog documents the changes between release versions.
## [Unreleased]
Changes to be included in the next upcoming release

## [0.0.28] - 2024-07-18
* Update SDK
* Provide template functions.py file

## [0.0.27] - 2024-07-18
Update SDK and fix Dockerfile

Expand Down
44 changes: 19 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,39 +1,33 @@
### Proof of concept NDC lambda connector for Python.
### Hasura Python Lambda Connector

This is a work in progress.
This connector allows you to write Python code and call it using Hasura!

To see a more in-depth example that implements the Python SDK please see: https://github.com/hasura/ndc-turso-python
With Hasura, you can integrate -- and even host -- this business logic directly with Hasura DDN and your API.

Currently, the proposed functionality will look something like this:
You can handle custom business logic using the Python Lambda data connector. Using this connector, you can transform or enrich data before it reaches your customers, or perform any other business logic you may need.

```python
from hasura_ndc_lambda import FunctionConnector, start
You can then integrate these functions as individual commands in your metadata and reulsting API.
This process enables you to simplify client applications and speed up your backend development!

connector = FunctionConnector()
## Setting up the Python Lambda connector

### Prerequisites:
In order to follow along with this guide, you will need:
* [The DDN CLI, VS Code extension, and Docker installed](https://hasura.io/docs/3.0/getting-started/build/prerequisites/)
* Python version `>= 3.11`

@connector.register_query
def do_the_thing(x: int) -> str:
print(x)
return "Hello World"
In this guide we will setup a new Hasura DDN project from scratch.

### Step-by-step guide

@connector.register_mutation
def some_mutation_function(arg1: str, arg2: int) -> str:
# Mutation function implementation
return f"Hey {arg1} {arg2}"
Create a new directory that will contain your Hasura project and change directories into it.

```mkdir ddn && cd ddn```

if __name__ == "__main__":
start(connector)
```
Create a new supergraph:

There will be support for built-in scalar types (int, float, str, bool) and also planned support for Pydantic types.
```ddn supergraph init --dir .```

Start a watch session, additionally split of a new terminal to continue running commands from.

## TODO: Allow adding of async queries/mutations?

## TODO: Add Pydantic type introspections


python3 main.py serve --configuration . --port 8087
```HASURA_DDN_PAT=$(ddn auth print-pat) docker compose up --build --watch```
109 changes: 77 additions & 32 deletions connector-definition/template/functions.py
Original file line number Diff line number Diff line change
@@ -1,50 +1,95 @@
"""
functions.py
This is an example of how you can use the Python SDK's built-in Function connector to easily write Python code.
When you add a Python Lambda connector to your Hasura project, this file is generated for you!
In this file you'll find code examples that will help you get up to speed with the usage of the Hasura lambda connector.
If you are an old pro and already know what is going on you can get rid of these example functions and start writing your own code.
"""
from hasura_ndc import start
from hasura_ndc.instrumentation import with_active_span
from opentelemetry.trace import get_tracer
from hasura_ndc.instrumentation import with_active_span # If you aren't planning on adding additional tracing spans, you don't need this!
from opentelemetry.trace import get_tracer # If you aren't planning on adding additional tracing spans, you don't need this either!
from hasura_ndc.function_connector import FunctionConnector
from pydantic import BaseModel
from pydantic import BaseModel # You only need this import if you plan to have complex inputs/outputs, which function similar to how frameworks like FastAPI do

connector = FunctionConnector()
tracer = get_tracer("ndc-sdk-python.server")

# This is an example of a simple function that can be added onto the graph
@connector.register_query # This is how you register a query
def hello(name: str) -> str:
return f"Hello {name}"

# You can use Nullable parameters, but they must default to None
# The FunctionConnector also doesn't care if your functions are sync or async, so use whichever you need!
@connector.register_query
def do_the_thing(x: int):
return f"Hello World {x}"
async def nullable_hello(name: str | None = None) -> str:
return f"Hello {name if name is not None else 'world'}"

# Parameters that are untyped accept any scalar type, arrays, or null and are treated as JSON.
# Untyped responses or responses with indeterminate types are treated as JSON as well!
@connector.register_mutation # This is how you register a mutation
def some_mutation_function(any_type_param):
return any_type_param

@connector.register_mutation
def some_mutation_function(arg1: str,
arg2: int):
return f"Hey {arg1} {arg2}"
# Similar to frameworks like FastAPI, you can use Pydantic Models for inputs and outputs
class Pet(BaseModel):
name: str

class Person(BaseModel):
name: str
pets: list[Pet] | None = None

@connector.register_query
async def my_query(x: str) -> str:
return await with_active_span(
tracer,
"My Span",
lambda span: f"My string is {x}",
{"attr": "value"}
)
def greet_person(person: Person) -> str:
greeting = f"Hello {person.name}!"
if person.pets is not None:
for pet in person.pets:
greeting += f" And hello to {pet.name}.."
else:
greeting += f" I see you don't have any pets."
return greeting

class ComplexType(BaseModel):
lists: list[list] # This turns into a List of List's of any valid JSON!
person: Person | None = None # This becomes a nullable attribute that accepts a person type from above
x: int # You can also use integers
y: float # As well as floats
z: bool # And booleans

# When the outputs are typed with Pydantic models you can select which attributes you want returned!
@connector.register_query
async def my_query2(x: str):
async def f(span):
# return f"My string is {x}"
return {
"hey": "x",
"var": x,
10.1: 10.1,
"dict": {
1.0: 10
},
"floatables": [1.234, 10, "yep"]
}
def complex_function(input: ComplexType) -> ComplexType:
return input

# This last section shows you how to add Otel tracing to any of your functions!
tracer = get_tracer("ndc-sdk-python.server") # You only need a tracer if you plan to add additional Otel spans

# Utilizing with_active_span allows the programmer to add Otel tracing spans
@connector.register_query
async def with_tracing(name: str) -> str:

def do_some_more_work(_span, work_response):
return f"Hello {name}, {work_response}"

async def the_async_work_to_do():
# This isn't actually async work, but it could be! Perhaps a network call belongs here, the power is in your hands fellow programmer!
return "That was a lot of work we did!"

async def do_some_async_work(_span):
work_response = await the_async_work_to_do()
return await with_active_span(
tracer,
"Sync Work Span",
lambda span: do_some_more_work(span, work_response), # Spans can wrap synchronous functions, and they can be nested for fine-grained tracing
{"attr": "sync work attribute"}
)

return await with_active_span(
tracer,
"My Span",
f,
{"attr": "value"}
"Root Span that does some async work",
do_some_async_work, # Spans can wrap asynchronous functions
{"tracing-attr": "Additional attributes can be added to Otel spans by making use of with_active_span like this"}
)


Expand Down
2 changes: 1 addition & 1 deletion connector-definition/template/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ fastapi==0.110.2
googleapis-common-protos==1.63.0
grpcio==1.62.2
h11==0.14.0
hasura-ndc==0.17
hasura-ndc==0.18
idna==3.7
importlib-metadata==7.0.0
opentelemetry-api==1.24.0
Expand Down

0 comments on commit 5324c43

Please sign in to comment.