From d042d5e95a86d9ad9e77abc3dc6f6a37dea99a57 Mon Sep 17 00:00:00 2001 From: Jonathan Kim Date: Thu, 9 Jul 2020 17:55:27 +0100 Subject: [PATCH] Expose Base64 type and add custom scalar examples (#1223) --- docs/api/index.rst | 2 + docs/types/scalars.rst | 185 +++++++++++++++++++++++++++++++++++-- graphene/__init__.py | 130 +++++++++++++------------- graphene/types/__init__.py | 69 +++++++------- 4 files changed, 279 insertions(+), 107 deletions(-) diff --git a/docs/api/index.rst b/docs/api/index.rst index 0da427e4..c5e3b6e1 100644 --- a/docs/api/index.rst +++ b/docs/api/index.rst @@ -64,6 +64,8 @@ Graphene Scalars .. autoclass:: graphene.JSONString() +.. autoclass:: graphene.Base64() + Enum ---- diff --git a/docs/types/scalars.rst b/docs/types/scalars.rst index 74002483..db20a522 100644 --- a/docs/types/scalars.rst +++ b/docs/types/scalars.rst @@ -3,6 +3,11 @@ Scalars ======= +Scalar types represent concrete values at the leaves of a query. There are +several built in types that Graphene provides out of the box which represent common +values in Python. You can also create your own Scalar types to better express +values that you might have in your data model. + All Scalar types accept the following arguments. All are optional: ``name``: *string* @@ -27,34 +32,39 @@ All Scalar types accept the following arguments. All are optional: -Base scalars ------------- +Built in scalars +---------------- -Graphene defines the following base Scalar Types: +Graphene defines the following base Scalar Types that match the default `GraphQL types `_: ``graphene.String`` +^^^^^^^^^^^^^^^^^^^ Represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text. ``graphene.Int`` +^^^^^^^^^^^^^^^^ Represents non-fractional signed whole numeric values. Int is a signed 32‐bit integer per the `GraphQL spec `_ ``graphene.Float`` +^^^^^^^^^^^^^^^^^^ Represents signed double-precision fractional values as specified by `IEEE 754 `_. ``graphene.Boolean`` +^^^^^^^^^^^^^^^^^^^^ Represents `true` or `false`. ``graphene.ID`` +^^^^^^^^^^^^^^^ Represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON @@ -62,24 +72,183 @@ Graphene defines the following base Scalar Types: When expected as an input type, any string (such as `"4"`) or integer (such as `4`) input value will be accepted as an ID. -Graphene also provides custom scalars for Dates, Times, and JSON: +---- -``graphene.types.datetime.Date`` +Graphene also provides custom scalars for common values: + +``graphene.Date`` +^^^^^^^^^^^^^^^^^ Represents a Date value as specified by `iso8601 `_. -``graphene.types.datetime.DateTime`` +.. code:: python + + import datetime + from graphene import Schema, ObjectType, Date + + class Query(ObjectType): + one_week_from = Date(required=True, date_input=Date(required=True)) + + def resolve_one_week_from(root, info, date_input): + assert date_input == datetime.date(2006, 1, 2) + return date_input + datetime.timedelta(weeks=1) + + schema = Schema(query=Query) + + results = schema.execute(""" + query { + oneWeekFrom(dateInput: "2006-01-02") + } + """) + + assert results.data == {"oneWeekFrom": "2006-01-09"} + + +``graphene.DateTime`` +^^^^^^^^^^^^^^^^^^^^^ Represents a DateTime value as specified by `iso8601 `_. -``graphene.types.datetime.Time`` +.. code:: python + + import datetime + from graphene import Schema, ObjectType, DateTime + + class Query(ObjectType): + one_hour_from = DateTime(required=True, datetime_input=DateTime(required=True)) + + def resolve_one_hour_from(root, info, datetime_input): + assert datetime_input == datetime.datetime(2006, 1, 2, 15, 4, 5) + return datetime_input + datetime.timedelta(hours=1) + + schema = Schema(query=Query) + + results = schema.execute(""" + query { + oneHourFrom(datetimeInput: "2006-01-02T15:04:05") + } + """) + + assert results.data == {"oneHourFrom": "2006-01-02T16:04:05"} + +``graphene.Time`` +^^^^^^^^^^^^^^^^^ Represents a Time value as specified by `iso8601 `_. -``graphene.types.json.JSONString`` +.. code:: python + + import datetime + from graphene import Schema, ObjectType, Time + + class Query(ObjectType): + one_hour_from = Time(required=True, time_input=Time(required=True)) + + def resolve_one_hour_from(root, info, time_input): + assert time_input == datetime.time(15, 4, 5) + tmp_time_input = datetime.datetime.combine(datetime.date(1, 1, 1), time_input) + return (tmp_time_input + datetime.timedelta(hours=1)).time() + + schema = Schema(query=Query) + + results = schema.execute(""" + query { + oneHourFrom(timeInput: "15:04:05") + } + """) + + assert results.data == {"oneHourFrom": "16:04:05"} + +``graphene.Decimal`` +^^^^^^^^^^^^^^^^^^^^ + + Represents a Python Decimal value. + +.. code:: python + + import decimal + from graphene import Schema, ObjectType, Decimal + + class Query(ObjectType): + add_one_to = Decimal(required=True, decimal_input=Decimal(required=True)) + + def resolve_add_one_to(root, info, decimal_input): + assert decimal_input == decimal.Decimal("10.50") + return decimal_input + decimal.Decimal("1") + + schema = Schema(query=Query) + + results = schema.execute(""" + query { + addOneTo(decimalInput: "10.50") + } + """) + + assert results.data == {"addOneTo": "11.50"} + +``graphene.JSONString`` +^^^^^^^^^^^^^^^^^^^^^^^ Represents a JSON string. +.. code:: python + + from graphene import Schema, ObjectType, JSONString, String + + class Query(ObjectType): + update_json_key = JSONString( + required=True, + json_input=JSONString(required=True), + key=String(required=True), + value=String(required=True) + ) + + def resolve_update_json_key(root, info, json_input, key, value): + assert json_input == {"name": "Jane"} + json_input[key] = value + return json_input + + schema = Schema(query=Query) + + results = schema.execute(""" + query { + updateJsonKey(jsonInput: "{\\"name\\": \\"Jane\\"}", key: "name", value: "Beth") + } + """) + + assert results.data == {"updateJsonKey": "{\"name\": \"Beth\"}"} + + +``graphene.Base64`` +^^^^^^^^^^^^^^^^^^^ + + Represents a Base64 encoded string. + +.. code:: python + + from graphene import Schema, ObjectType, Base64 + + class Query(ObjectType): + increment_encoded_id = Base64( + required=True, + base64_input=Base64(required=True), + ) + + def resolve_increment_encoded_id(root, info, base64_input): + assert base64_input == "4" + return int(base64_input) + 1 + + schema = Schema(query=Query) + + results = schema.execute(""" + query { + incrementEncodedId(base64Input: "NA==") + } + """) + + assert results.data == {"incrementEncodedId": "NQ=="} + + Custom scalars -------------- diff --git a/graphene/__init__.py b/graphene/__init__.py index a59347dd..13eb375d 100644 --- a/graphene/__init__.py +++ b/graphene/__init__.py @@ -1,46 +1,45 @@ from .pyutils.version import get_version - -from .types import ( - ObjectType, - InputObjectType, - Interface, - Mutation, - Field, - InputField, - Schema, - Scalar, - String, - ID, - Int, - Float, - Boolean, - Date, - DateTime, - Time, - Decimal, - JSONString, - UUID, - List, - NonNull, - Enum, - Argument, - Dynamic, - Union, - Context, - ResolveInfo, -) from .relay import ( - Node, - is_node, - GlobalID, ClientIDMutation, Connection, ConnectionField, + GlobalID, + Node, PageInfo, + is_node, +) +from .types import ( + ID, + UUID, + Argument, + Base64, + Boolean, + Context, + Date, + DateTime, + Decimal, + Dynamic, + Enum, + Field, + Float, + InputField, + InputObjectType, + Int, + Interface, + JSONString, + List, + Mutation, + NonNull, + ObjectType, + ResolveInfo, + Scalar, + Schema, + String, + Time, + Union, ) -from .utils.resolve_only_args import resolve_only_args from .utils.module_loading import lazy_import - +from .utils.resolve_only_args import resolve_only_args VERSION = (3, 0, 0, "beta", 3) @@ -49,40 +48,41 @@ __version__ = get_version(VERSION) __all__ = [ "__version__", - "ObjectType", - "InputObjectType", - "Interface", - "Mutation", - "Field", - "InputField", - "Schema", - "Scalar", - "String", - "ID", - "Int", - "Float", - "Enum", - "Boolean", - "Date", - "DateTime", - "Time", - "Decimal", - "JSONString", - "UUID", - "List", - "NonNull", "Argument", - "Dynamic", - "Union", - "resolve_only_args", - "Node", - "is_node", - "GlobalID", + "Base64", + "Boolean", "ClientIDMutation", "Connection", "ConnectionField", - "PageInfo", - "lazy_import", "Context", + "Date", + "DateTime", + "Decimal", + "Dynamic", + "Enum", + "Field", + "Float", + "GlobalID", + "ID", + "InputField", + "InputObjectType", + "Int", + "Interface", + "JSONString", + "List", + "Mutation", + "Node", + "NonNull", + "ObjectType", + "PageInfo", "ResolveInfo", + "Scalar", + "Schema", + "String", + "Time", + "UUID", + "Union", + "is_node", + "lazy_import", + "resolve_only_args", ] diff --git a/graphene/types/__init__.py b/graphene/types/__init__.py index 680149a3..2641dd53 100644 --- a/graphene/types/__init__.py +++ b/graphene/types/__init__.py @@ -1,52 +1,53 @@ # flake8: noqa from graphql import GraphQLResolveInfo as ResolveInfo -from .objecttype import ObjectType -from .interface import Interface -from .mutation import Mutation -from .scalars import Scalar, String, ID, Int, Float, Boolean +from .argument import Argument +from .base64 import Base64 +from .context import Context from .datetime import Date, DateTime, Time from .decimal import Decimal -from .json import JSONString -from .uuid import UUID -from .schema import Schema -from .structures import List, NonNull +from .dynamic import Dynamic from .enum import Enum from .field import Field from .inputfield import InputField -from .argument import Argument from .inputobjecttype import InputObjectType -from .dynamic import Dynamic +from .interface import Interface +from .json import JSONString +from .mutation import Mutation +from .objecttype import ObjectType +from .scalars import ID, Boolean, Float, Int, Scalar, String +from .schema import Schema +from .structures import List, NonNull from .union import Union -from .context import Context - +from .uuid import UUID __all__ = [ - "ObjectType", - "InputObjectType", - "Interface", - "Mutation", - "Enum", - "Field", - "InputField", - "Schema", - "Scalar", - "String", - "ID", - "Int", - "Float", + "Argument", + "Base64", + "Boolean", + "Context", "Date", "DateTime", - "Time", "Decimal", - "JSONString", - "UUID", - "Boolean", - "List", - "NonNull", - "Argument", "Dynamic", - "Union", - "Context", + "Enum", + "Field", + "Float", + "ID", + "InputField", + "InputObjectType", + "Int", + "Interface", + "JSONString", + "List", + "Mutation", + "NonNull", + "ObjectType", "ResolveInfo", + "Scalar", + "Schema", + "String", + "Time", + "UUID", + "Union", ]