Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
407 changes: 31 additions & 376 deletions README.md

Large diffs are not rendered by default.

39 changes: 39 additions & 0 deletions docs/default_get_doc_get_list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
## Getting single Document and getting a filtered doctype list

You can get a single document by its name using `<doctype>` query.
For sort by fields, only those fields that are search_indexed / unique can be used. NAME, CREATION & MODIFIED can also be used

### Query

```
{
User(name: "Administrator") {
name,
email
}
}
```

You can get a list of documents by querying `<doctype-plural>`. You can also pass in filters and sorting details as arguments:

```graphql
{
Users(filter: [["name", "like", "%a%"]], sortBy: { field: NAME, direction: ASC }) {
totalCount,
pageInfo {
hasNextPage,
hasPreviousPage,
startCursor,
endCursor
},
edges {
cursor,
node {
name,
first_name
}
}
}
}
}
```
25 changes: 25 additions & 0 deletions docs/examples/add_document_count_query.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Adding a DocMeta Query

```py
# Add the cmd to the following function in `graphql_schema_processors`
def docmeta_query(schema):
from graphql import GraphQLField, GraphQLObjectType, GraphQLString, GraphQLInt
schema.query_type.fields["docmeta"] = GraphQLField(
GraphQLList(GraphQLObjectType(
name="DocTypeMeta",
fields={
"name": GraphQLField(
GraphQLString,
resolve=lambda obj, info: obj
),
"number_of_docs": GraphQLField(
GraphQLInt,
resolve=lambda obj, info: frappe.db.count(obj)
),
}
)),
resolve=lambda obj, info: [x.name for x in frappe.get_all("DocType")]
)
```

Please refer `graphql-core` for more examples
43 changes: 43 additions & 0 deletions docs/examples/add_hello_world_query.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Adding a Hello World Query

- Add a cmd in `graphql_schema_processors` hook
- Use the following function definition for the cmd specified:

```py
def hello_world(schema: GraphQLSchema):

def hello_resolver(obj, info: GraphQLResolveInfo, **kwargs):
return f"Hello {kwargs.get('name')}!"

schema.query_type.fields["hello"] = GraphQLField(
GraphQLString,
resolve=hello_resolver,
args={
"name": GraphQLArgument(
type_=GraphQLString,
default_value="World"
)
}
)
```

- Now, you can query like query like this:

```py
# Request
query Hello($var_name: String) {
hello(name: $var_name)
}

# Variables
{
"var_name": "Mars"
}

# Response
{
"data": {
"hello": "Hello Mars!"
}
}
```
10 changes: 10 additions & 0 deletions docs/examples/add_new_custom_field.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
## Introducing a Custom Field to GraphQL

- Add the `Custom Field` in frappe
- Add the following to a `.graphql` file in your app and specify its directory via `graphql_sdl_dir`

```graphql
extend type User {
is_super: Int!
}
```
19 changes: 19 additions & 0 deletions docs/examples/add_new_doctype.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
## Adding a newly created DocType

- Generate the SDLs in your app directory

```bash
# Generate SDLs for all doctypes in <your-app>
$ bench --site test_site graphql generate_sdl --output-dir <your-app/graphql> --app <your-app>

# Generate SDLs for all doctype in module <m1> <m2>
$ bench --site test_site graphql generate_sdl --output-dir <your-app/graphql> --module <m1> -m <m2> -m <name>

# Generate SDLs for doctype <d1> <2>
$ bench --site test_site graphql generate_sdl --output-dir <your-app/graphql> --doctype <d1> -dt <d2> -dt <name>

# Generate SDLs for all doctypes in <your-app> without Enums for Select Fields
$ bench --site test_site graphql generate_sdl --output-dir <your-app/graphql> --app <your-app> --disable-enum-select-fields
```

- Specify this directory in `graphql_sdl_dir` hook and you are done.
56 changes: 56 additions & 0 deletions docs/examples/add_new_mutation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Adding a new Mutation

There are two ways:

1. Write SDL and Attach Resolver to the Schema

```graphql
# SDL for Mutation
type MY_MUTATION_OUTPUT_TYPE {
success: Boolean
}

extend type Mutation {
myNewMutation(args): MY_MUTATION_OUTPUT_TYPE
}
```

```py
# Attach Resolver (Specify the cmd to this function in `graphql_schema_processors` hook)
def myMutationResolver(schema: GraphQLSchema):
def _myMutationResolver(obj: Any, info: GraphQLResolveInfo):
# frappe.set_value(..)
return {
"success": True
}

mutation_type = schema.mutation_type
mutation_type.fields["myNewMutation"].resolve = _myMutationResolver
```

2. Make use of `graphql-core` apis

```py
# Specify the cmd to this function in `graphql_schema_processors` hook
def bindMyNewMutation(schema):

def _myMutationResolver(obj: Any, info: GraphQLResolveInfo):
# frappe.set_value(..)
return {
"success": True
}

mutation_type = schema.mutation_type
mutation_type.fields["myNewMutation"] = GraphQLField(
GraphQLObjectType(
name="MY_MUTATION_OUTPUT_TYPE",
fields={
"success": GraphQLField(
GraphQLBoolean,
resolve=lambda obj, info: obj["success"]
)
}
),
resolve=_myMutationResolver
)
```
21 changes: 21 additions & 0 deletions docs/extending_schema.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
## Support Extensions via Hooks

You can extend the SDLs with additional query / mutations / subscriptions. Examples are provided for a specific set of Scenarios. Please read [GraphQL Spec](http://spec.graphql.org/June2018/#sec-Object-Extensions) regarding Extending types. There are mainly two hooks introduced:

- `graphql_sdl_dir`
Specify a list of directories containing `.graphql` files relative to the app's root directory.
eg:

```py
# hooks.py
graphql_sdl_dir = [
"./your-app/your-app/generated/sdl/dir1",
"./your-app/your-app/generated/sdl/dir2",
]
```

The above will look for graphql files in `your-bench/apps/your-app/your-app/generated/sdl/dir1` & `./dir2` folders.

- `graphql_schema_processors`
You can pass in a list of cmd that gets executed on schema creation. You are given `GraphQLSchema` object (please refer [graphql-core](https://github.com/graphql-python/graphql-core)) as the only parameter. You can modify it or extend it as per your requirements.
This is a good place to attach the resolvers for the custom SDLs defined via `graphql_sdl_dir`
47 changes: 47 additions & 0 deletions docs/file_uploads.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
## File Uploads

File uploads can be done following the [GraphQL multipart request specification](https://github.com/jaydenseric/graphql-multipart-request-spec). `uploadFile` mutation is included implementing the same

### Query

```http
POST /api/method/graphql HTTP/1.1
Host: test_site:8000
Accept: application/json
Cookie: full_name=Administrator; sid=<sid>; system_user=yes; user_id=Administrator; user_image=
Content-Length: 553
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="operations"

{
"query": "mutation uploadFile($file: Upload!) { uploadFile(file: $file) { name, file_url } }",
"variables": {
"file": null
}
}
----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="map"

{ "0": ["variables.file"] }
----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="0"; filename="/D:/faztp12/Pictures/BingImageOfTheDay_20190715.jpg"
Content-Type: image/jpeg

(data)
----WebKitFormBoundary7MA4YWxkTrZu0gW
```

Response

```json
{
"data": {
"uploadFile": {
"name": "ce36b2e222",
"file_url": "/files/BingImageOfTheDay_20190715.jpg"
}
}
}
```
56 changes: 56 additions & 0 deletions docs/link_fields_nested.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
## Access Field Linked Documents in nested queries

All Link fields return respective doc. Add `__name` suffix to the link field name to get the link name.

### Query

```gql
{
ToDo(limit_page_length: 1) {
name
priority
description
assigned_by__name
assigned_by {
full_name
roles {
role__name
role {
name
creation
}
}
}
}
}
```

### Result

```json
{
"data": {
"ToDo": [
{
"name": "ae6f39845b",
"priority": "Low",
"description": "<div class=\"ql-editor read-mode\"><p>Do this</p></div>",
"assigned_by__name": "Administrator",
"assigned_by": {
"full_name": "Administrator",
"roles": [
{
"role__name": "System Manager",
"role": {
"name": "System Manager",
"creation": "2021-02-02 08:34:42.170306",
}
}
]
}
}
...
]
}
}
```
23 changes: 23 additions & 0 deletions docs/middleware.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
## Support Extension of Middlewares via hooks

We can add graphql middlewares by adding the path through hooks.
Please note the return type and arguments being passed to your custom middleware.

```py
# hooks.py
graphql_middlewares = ["frappe_graphql.utils.middlewares.disable_introspection_queries.disable_introspection_queries"]
```

```py
def disable_introspection_queries(next_resolver, obj, info: GraphQLResolveInfo, **kwargs):
# https://github.com/jstacoder/graphene-disable-introspection-middleware
if is_introspection_disabled() and info.field_name.lower() in ['__schema', '__introspection']:
raise IntrospectionDisabled(frappe._("Introspection is disabled"))

return next_resolver(obj, info, **kwargs)


def is_introspection_disabled():
return not cint(frappe.local.conf.get("developer_mode")) and \
not cint(frappe.local.conf.get("enable_introspection_in_production"))
```
5 changes: 5 additions & 0 deletions docs/restrict_depth.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## Restrict Query/Mutation depth

Query/Mutation is restricted by default to 10.

You can change the depth limit by setting the site config `frappe_graphql_depth_limit: 15`.