Skip to content

Commit 5324c43

Browse files
author
Tristen Harr
committed
update functions template and SDK
1 parent 4678b62 commit 5324c43

File tree

4 files changed

+101
-58
lines changed

4 files changed

+101
-58
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ This changelog documents the changes between release versions.
44
## [Unreleased]
55
Changes to be included in the next upcoming release
66

7+
## [0.0.28] - 2024-07-18
8+
* Update SDK
9+
* Provide template functions.py file
10+
711
## [0.0.27] - 2024-07-18
812
Update SDK and fix Dockerfile
913

README.md

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,33 @@
1-
### Proof of concept NDC lambda connector for Python.
1+
### Hasura Python Lambda Connector
22

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

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

7-
Currently, the proposed functionality will look something like this:
7+
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.
88

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

12-
connector = FunctionConnector()
12+
## Setting up the Python Lambda connector
1313

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

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

21+
### Step-by-step guide
2022

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

25+
```mkdir ddn && cd ddn```
2626

27-
if __name__ == "__main__":
28-
start(connector)
29-
```
27+
Create a new supergraph:
3028

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

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

34-
## TODO: Allow adding of async queries/mutations?
35-
36-
## TODO: Add Pydantic type introspections
37-
38-
39-
python3 main.py serve --configuration . --port 8087
33+
```HASURA_DDN_PAT=$(ddn auth print-pat) docker compose up --build --watch```

connector-definition/template/functions.py

Lines changed: 77 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,95 @@
1+
"""
2+
functions.py
3+
4+
This is an example of how you can use the Python SDK's built-in Function connector to easily write Python code.
5+
When you add a Python Lambda connector to your Hasura project, this file is generated for you!
6+
7+
In this file you'll find code examples that will help you get up to speed with the usage of the Hasura lambda connector.
8+
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.
9+
"""
110
from hasura_ndc import start
2-
from hasura_ndc.instrumentation import with_active_span
3-
from opentelemetry.trace import get_tracer
11+
from hasura_ndc.instrumentation import with_active_span # If you aren't planning on adding additional tracing spans, you don't need this!
12+
from opentelemetry.trace import get_tracer # If you aren't planning on adding additional tracing spans, you don't need this either!
413
from hasura_ndc.function_connector import FunctionConnector
5-
from pydantic import BaseModel
14+
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
615

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

18+
# This is an example of a simple function that can be added onto the graph
19+
@connector.register_query # This is how you register a query
20+
def hello(name: str) -> str:
21+
return f"Hello {name}"
22+
23+
# You can use Nullable parameters, but they must default to None
24+
# The FunctionConnector also doesn't care if your functions are sync or async, so use whichever you need!
1025
@connector.register_query
11-
def do_the_thing(x: int):
12-
return f"Hello World {x}"
26+
async def nullable_hello(name: str | None = None) -> str:
27+
return f"Hello {name if name is not None else 'world'}"
1328

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

15-
@connector.register_mutation
16-
def some_mutation_function(arg1: str,
17-
arg2: int):
18-
return f"Hey {arg1} {arg2}"
35+
# Similar to frameworks like FastAPI, you can use Pydantic Models for inputs and outputs
36+
class Pet(BaseModel):
37+
name: str
38+
39+
class Person(BaseModel):
40+
name: str
41+
pets: list[Pet] | None = None
1942

2043
@connector.register_query
21-
async def my_query(x: str) -> str:
22-
return await with_active_span(
23-
tracer,
24-
"My Span",
25-
lambda span: f"My string is {x}",
26-
{"attr": "value"}
27-
)
44+
def greet_person(person: Person) -> str:
45+
greeting = f"Hello {person.name}!"
46+
if person.pets is not None:
47+
for pet in person.pets:
48+
greeting += f" And hello to {pet.name}.."
49+
else:
50+
greeting += f" I see you don't have any pets."
51+
return greeting
52+
53+
class ComplexType(BaseModel):
54+
lists: list[list] # This turns into a List of List's of any valid JSON!
55+
person: Person | None = None # This becomes a nullable attribute that accepts a person type from above
56+
x: int # You can also use integers
57+
y: float # As well as floats
58+
z: bool # And booleans
2859

60+
# When the outputs are typed with Pydantic models you can select which attributes you want returned!
2961
@connector.register_query
30-
async def my_query2(x: str):
31-
async def f(span):
32-
# return f"My string is {x}"
33-
return {
34-
"hey": "x",
35-
"var": x,
36-
10.1: 10.1,
37-
"dict": {
38-
1.0: 10
39-
},
40-
"floatables": [1.234, 10, "yep"]
41-
}
62+
def complex_function(input: ComplexType) -> ComplexType:
63+
return input
64+
65+
# This last section shows you how to add Otel tracing to any of your functions!
66+
tracer = get_tracer("ndc-sdk-python.server") # You only need a tracer if you plan to add additional Otel spans
67+
68+
# Utilizing with_active_span allows the programmer to add Otel tracing spans
69+
@connector.register_query
70+
async def with_tracing(name: str) -> str:
71+
72+
def do_some_more_work(_span, work_response):
73+
return f"Hello {name}, {work_response}"
74+
75+
async def the_async_work_to_do():
76+
# This isn't actually async work, but it could be! Perhaps a network call belongs here, the power is in your hands fellow programmer!
77+
return "That was a lot of work we did!"
78+
79+
async def do_some_async_work(_span):
80+
work_response = await the_async_work_to_do()
81+
return await with_active_span(
82+
tracer,
83+
"Sync Work Span",
84+
lambda span: do_some_more_work(span, work_response), # Spans can wrap synchronous functions, and they can be nested for fine-grained tracing
85+
{"attr": "sync work attribute"}
86+
)
4287

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

5095

connector-definition/template/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ fastapi==0.110.2
99
googleapis-common-protos==1.63.0
1010
grpcio==1.62.2
1111
h11==0.14.0
12-
hasura-ndc==0.17
12+
hasura-ndc==0.18
1313
idna==3.7
1414
importlib-metadata==7.0.0
1515
opentelemetry-api==1.24.0

0 commit comments

Comments
 (0)