Skip to content

Commit af60e1d

Browse files
Initial commit
0 parents  commit af60e1d

19 files changed

Lines changed: 1261 additions & 0 deletions

.github/workflows/code_quality.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: Code Quality
2+
3+
on:
4+
push:
5+
branches:
6+
- "*"
7+
tags:
8+
- "*"
9+
10+
jobs:
11+
lint:
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- uses: actions/checkout@v2
16+
17+
- name: Set up Python
18+
uses: actions/setup-python@v2
19+
with:
20+
python-version: "3.12"
21+
22+
- name: Install dependencies
23+
run: |
24+
python -m pip install --upgrade pip
25+
pip install hatch
26+
27+
- name: Build the package
28+
run: |
29+
hatch build
30+
31+
- name: Run linter
32+
run: |
33+
hatch run ruff check .

.github/workflows/deploy.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: Publish on PyPI
2+
3+
on:
4+
release:
5+
types:
6+
- published
7+
8+
jobs:
9+
build-and-publish:
10+
name: Build and publish to PyPI
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- uses: actions/checkout@master
15+
16+
- name: Set up Python
17+
uses: actions/setup-python@v4
18+
with:
19+
python-version: "3.12"
20+
21+
- name: Install Hatch
22+
run: |
23+
python -m pip install --upgrade pip
24+
pip install hatch
25+
26+
- name: Build with Hatch
27+
run: |
28+
hatch build
29+
30+
- name: Publish distribution to PyPI
31+
uses: pypa/gh-action-pypi-publish@v1.4.2
32+
with:
33+
user: __token__
34+
password: ${{ secrets.PYPI_API_TOKEN }}
35+
packages_dir: dist
36+
verbose: true
37+
38+
- name: Clean distribution directory
39+
run: rm -rf dist/*

.github/workflows/run_tests.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: Run Tests
2+
3+
on:
4+
push:
5+
branches:
6+
- "*"
7+
tags:
8+
- "*"
9+
10+
jobs:
11+
build-and-test:
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- uses: actions/checkout@v2
16+
17+
- name: Set up Python
18+
uses: actions/setup-python@v2
19+
with:
20+
python-version: "3.12"
21+
22+
- name: Install dependencies
23+
run: |
24+
python -m pip install --upgrade pip
25+
pip install hatch
26+
27+
- name: Build the package
28+
run: |
29+
hatch build
30+
31+
- name: Run tests
32+
run: |
33+
hatch run test

.gitignore

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Distribution / packaging
2+
.Python
3+
env/
4+
build/
5+
develop-eggs/
6+
dist/
7+
downloads/
8+
eggs/
9+
.eggs/
10+
lib/
11+
lib64/
12+
parts/
13+
sdist/
14+
var/
15+
*.egg-info/
16+
.installed.cfg
17+
*.egg
18+
19+
# Serverless directories
20+
.serverless
21+
22+
.ruff_cache/
23+
node_modules/
24+
__pycache__
25+
26+
.DS_Store
27+
.env
28+
.pytest_cache
29+
.python-version
30+
31+
package
32+
*.zip
33+
34+
# Ignore the .terraform directory
35+
.terraform/
36+
37+
# Ignore Terraform lock files
38+
.terraform.lock.hcl
39+
40+
# Ignore all .tfstate files
41+
*.tfstate
42+
*.tfstate.*
43+
44+
# Ignore crash log files
45+
crash.log
46+
47+
# Ignore any .tfvars files that may contain sensitive data
48+
*.tfvars
49+
50+
# Ignore override files as they are usually used to override resources locally
51+
override.tf
52+
override.tf.json
53+
*_override.tf
54+
*_override.tf.json
55+
56+
# Ignore CLI configuration files
57+
.terraformrc
58+
terraform.rc
59+
60+
# pytest-cov
61+
.coverage

LICENSE

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
BSD 3-Clause License
2+
3+
Copyright (c) 2024, Mirumee Labs
4+
All rights reserved.
5+
6+
Redistribution and use in source and binary forms, with or without
7+
modification, are permitted provided that the following conditions are met:
8+
9+
* Redistributions of source code must retain the above copyright notice, this
10+
list of conditions and the following disclaimer.
11+
12+
* Redistributions in binary form must reproduce the above copyright notice,
13+
this list of conditions and the following disclaimer in the documentation
14+
and/or other materials provided with the distribution.
15+
16+
* Neither the name of the copyright holder nor the names of its
17+
contributors may be used to endorse or promote products derived from
18+
this software without specific prior written permission.
19+
20+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

README.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Ariadne AWS Lambda Extension
2+
3+
This package extends the Ariadne library by adding a GraphQL HTTP handler designed for use in AWS Lambda environments. It enables easy integration of GraphQL services with AWS serverless infrastructure, making it straightforward to deploy GraphQL APIs without worrying about the underlying server management.
4+
5+
## Introduction
6+
7+
This project provides an extension to the Ariadne GraphQL library, specifically tailored for deploying GraphQL APIs on AWS Lambda. It simplifies handling GraphQL requests by providing a custom HTTP handler that seamlessly integrates with the AWS Lambda and API Gateway, allowing developers to focus on their GraphQL schema and resolvers instead of server and infrastructure management.
8+
9+
## Installation
10+
11+
To install the extension, use pip:
12+
13+
```bash
14+
pip install ariadne-lambda
15+
```
16+
17+
## Quick Start
18+
19+
Here's a basic example of how to use the extension in your AWS Lambda function:
20+
21+
```python
22+
from typing import Any
23+
24+
from ariadne import QueryType, gql, make_executable_schema
25+
from ariadne_lambda.graphql import GraphQLLambda
26+
from asgiref.sync import async_to_sync
27+
from aws_lambda_powertools.utilities.typing import LambdaContext
28+
29+
type_defs = gql(
30+
"""
31+
type Query {
32+
hello: String!
33+
}
34+
"""
35+
)
36+
query = QueryType()
37+
38+
39+
@query.field("hello")
40+
def resolve_hello(_, info):
41+
request = info.context["request"]
42+
user_agent = request.headers.get("user-agent", "guest")
43+
return "Hello, %s!" % user_agent
44+
45+
46+
schema = make_executable_schema(type_defs, query)
47+
graphql_app = GraphQLLambda(schema=schema)
48+
49+
50+
def graphql_http_handler(event: dict[str, Any], context: LambdaContext):
51+
return async_to_sync(graphql_app)(event, context)
52+
```
53+
54+
## Documentation
55+
56+
For full documentation on Ariadne, visit [Ariadne's Documentation](https://ariadnegraphql.org/docs/). For details on AWS Lambda, refer to the [AWS Lambda Developer Guide](https://docs.aws.amazon.com/lambda/latest/dg/welcome.html).
57+
58+
## Features
59+
60+
- Easy integration with AWS Lambda and API Gateway.
61+
- Support for GraphQL queries and mutations.
62+
- Customizable context and error handling.
63+
- Seamless extension of the Ariadne library for serverless applications.
64+
65+
## Contributing
66+
67+
We welcome all contributions to Ariadne! If you've found a bug or issue, feel free to use [GitHub issues](https://github.com/mirumee/ariadne-lambda/issues). If you have any questions or feedback, don't hesitate to catch us on [GitHub discussions](https://github.com/mirumee/ariadne/discussions/).
68+
69+
For guidance and instructions, please see [CONTRIBUTING.md](CONTRIBUTING.md).
70+
71+
Also make sure you follow [@AriadneGraphQL](https://twitter.com/AriadneGraphQL) on Twitter for latest updates, news and random musings!
72+
73+
**Crafted with ❤️ by [Mirumee Software](http://mirumee.com)**
74+
hello@mirumee.com

ariadne_lambda/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from ariadne_lambda.graphql import GraphQLLambda
2+
from ariadne_lambda.http_handler import GraphQLAWSAPIHTTPGatewayHandler
3+
4+
__all__ = ["GraphQLLambda", "GraphQLAWSAPIHTTPGatewayHandler"]

ariadne_lambda/base.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
from abc import abstractmethod
2+
from inspect import isawaitable
3+
from typing import Any
4+
5+
from ariadne.asgi.handlers.base import GraphQLHandler
6+
from aws_lambda_powertools.utilities.typing import LambdaContext
7+
8+
9+
class GraphQLLambdaHandler(GraphQLHandler):
10+
@abstractmethod
11+
async def handle(self, event: dict, context: LambdaContext):
12+
"""An entrypoint for the AWS Lambda connection handler.
13+
14+
This method is called by Ariadne AWS Lambda GraphQL application. Subclasses
15+
are expected to handle specific event content based on the gateway of invocation
16+
triggeting the lambda function (e.g. API Gateway, ALB, etc.).
17+
18+
# Required arguments
19+
20+
`event`: The AWS Lambda event dictionary.
21+
22+
`context`: The AWS Lambda context object.
23+
"""
24+
raise NotImplementedError(
25+
"Subclasses of GraphQLLambdaHandler must implement the 'handle' method"
26+
)
27+
28+
async def get_context_for_request(
29+
self,
30+
request: Any,
31+
data: dict,
32+
) -> Any:
33+
"""Return the context value for the request.
34+
35+
This method is called by the handler to get the context value for the
36+
request. Subclasses can override it to provide custom context value
37+
based on the request.
38+
39+
# Required arguments
40+
41+
`request`: The request object as defined by the 'handle' method.
42+
43+
`data`: GraphQL data from connection.
44+
"""
45+
if callable(self.context_value):
46+
try:
47+
context = self.context_value(request, data) # type: ignore
48+
except TypeError: # TODO: remove in 0.20
49+
context = self.context_value(request) # type: ignore
50+
51+
if isawaitable(context):
52+
context = await context
53+
return context
54+
55+
return self.context_value or {"request": request}

0 commit comments

Comments
 (0)