def build_ValidationError(): ValidationErrorItem = g.ObjectType( 'ValidationErrorItem', fields=g.fields({ 'name': str, 'messages': g.NNList(g.String), }), ) def resolve_errors(obj, info): # note .error, obj is boxed with kocherga.django.errors.BoxedError return [{ 'name': k, 'messages': [str(e) for e in v] } for k, v in obj.error.message_dict.items()] return g.ObjectType( 'ValidationError', fields={ 'errors': g.Field( g.NNList(ValidationErrorItem), resolve=resolve_errors, ), }, )
def build_StructBlockValidationError(): FieldValidationError = g.ObjectType( 'WagtailStructBlockFieldValidationError', fields=lambda: g.fields({ 'name': str, 'error': WagtailBlockValidationError }), ) return g.ObjectType( 'WagtailStructBlockValidationError', fields=lambda: g.fields({ 'error_message': valdiation_error_message_field, 'errors': g.Field( g.NNList(FieldValidationError), resolve=lambda obj, info: [{ 'name': k, 'error': v } for k, v in obj.data[0].params.items()], ), }), interfaces=[WagtailBlockValidationError], )
def build_WagtailStreamFieldValidationError(): def resolve_block_errors(obj, info): result = [] for k, v in obj['params'].items(): assert isinstance(v, ErrorList) if k == '__all__': continue # non-block error result.append({'block_id': k, 'error': v}) return result def resolve_non_block_error(obj, info): error = obj['params'].get('__all__') if error is None: return None return str(error) return g.ObjectType( 'WagtailStreamFieldValidationError', g.fields({ 'block_errors': g.Field( g.NNList(WagtailStreamBlockValidationError), resolve=resolve_block_errors, ), 'non_block_error': g.Field(g.String, resolve=resolve_non_block_error), }), )
class checkRatioPromocode(helpers.BaseFieldWithInput): def resolve(self, _, info, input): try: ticket_type = models.TicketType.objects.get( uuid=input['ticket_type_id']) except models.TicketType.DoesNotExist: return None try: promocode = ticket_type.check_promocode(code=input['code']) except models.Promocode.DoesNotExist: return None if not promocode.is_valid(): return None return { 'discounted_price': promocode.check_apply(ticket_type.price), } permissions = [] input = { 'ticket_type_id': 'ID!', 'code': str, } result = g.ObjectType( 'CheckRatioPromocodeResult', g.fields({ 'discounted_price': int, }), )
def create_account_and_service_types(service_class, account_fields): Account = g.ObjectType( service_to_account_type(service_class), fields=lambda: g.fields({ 'service': g.NN(Service), **account_fields }), interfaces=[ExternalServiceAccount], ) Service = g.ObjectType( service_to_type(service_class), fields=service_fields(Account), interfaces=[ExternalService], ) return [Account, Service]
class authLogout(helpers.BaseField): permissions = [authenticated] result = g.NN( g.ObjectType('AuthLogoutResult', g.fields({'ok': Optional[bool]}))) def resolve(self, _, info): logout(info.context) return {'ok': True}
class vk_field(helpers.BaseField): def resolve(self, obj, info): return { 'link': obj.vk_link(), } permissions = [] result = g.NN( g.ObjectType('EventsWeeklyDigestVk', g.fields({'link': Optional[str]})))
def build_AnyBlockValidationError(): return g.ObjectType( 'WagtailAnyBlockValidationError', fields=lambda: g.fields({ 'error_message': g.Field( g.NN(g.String), resolve=lambda obj, info: getattr(obj.data[0], 'message', str(obj.data[0])), ), }), interfaces=[WagtailBlockValidationError], )
class wagtailPagePermissions(helpers.BaseField): # TODO - enum? PAGE_PERMISSION_TYPES is a constant in wagtail _permission_type = g.NN(g.String) WagtailSpecificPagePermission = g.ObjectType( 'WagtailSpecificPagePermission', fields=g.fields({ 'id': 'ID!', 'permission_type': _permission_type, 'page': g.NN(wagtail_types.WagtailPage), }), ) WagtailRootPagePermission = g.ObjectType( 'WagtailRootPagePermission', fields=g.fields({ 'id': 'ID!', 'permission_type': _permission_type, }), ) # TODO - interface instead of union? would be better for querying WagtailPagePermission = g.UnionType( 'WagtailPagePermission', types=[WagtailRootPagePermission, WagtailSpecificPagePermission], resolve_type=lambda obj, info, *_: ('WagtailRootPagePermission' if obj.page.is_root() else 'WagtailSpecificPagePermission'), ) # AuthGroup is very private so we don't need additional checks here permissions = [] def resolve(self, group, info): return group.page_permissions.all() result = g.NNList(WagtailPagePermission)
class mailchimp_field(helpers.BaseField): def resolve(self, obj, info): return { 'link': obj.mailchimp_campaign_link(), 'is_sent': obj.mailchimp_sent, } permissions = [] result = g.NN( g.ObjectType( 'EventsWeeklyDigestMailchimp', g.fields({ 'link': Optional[str], 'is_sent': bool, }), ))
def create_EventsListBlock(): def resolve_value(obj, info): qs = kocherga.events.models.Event.objects.public_only( ).filter_by_period(from_date=datetime.today()) return qs[:20] return g.ObjectType( 'EventsListBlock', interfaces=[WagtailBlock], fields=g.fields({ 'id': 'ID!', 'events': g.Field(g.NNList(event_types.Event), resolve=resolve_value), }), )
class wagtailCollectionPermissions(helpers.BaseField): WagtailCollectionPermission = g.ObjectType( 'WagtailCollectionPermission', fields=g.fields({ 'id': 'ID!', 'permission': g.NN(AuthPermission), 'collection': g.NN(wagtail_types.WagtailCollection), }), ) permissions = [] def resolve(self, group, info): return group.collection_permissions.all() result = g.NNList(WagtailCollectionPermission)
def build_ListBlockValidationError(): def resolve_errors(obj, info): return obj.data[0].params return g.ObjectType( 'WagtailListBlockValidationError', fields=lambda: g.fields({ 'error_message': valdiation_error_message_field, 'errors': g.Field( g.NN(g.List(WagtailBlockValidationError)), resolve=resolve_errors, ), }), interfaces=[WagtailBlockValidationError], )
def create_PhotoRibbonBlock(): def resolve_value(obj, info, spec): return [image.get_rendition(spec) for image in obj.value] return g.ObjectType( 'PhotoRibbonBlock', interfaces=[WagtailBlock], fields=g.fields({ 'id': 'ID!', 'value': g.Field( g.NNList(WagtailImageRendition), args=g.arguments({'spec': str}), resolve=resolve_value, ), }), )
class ratioConfirmOrder(helpers.BaseFieldWithInput): def resolve(self, _, info, input): order_id = input['order_id'] try: order = models.Order.objects.get(uuid=order_id) except models.Order.DoesNotExist: return {'outcome': self.OutcomeEnum.NOT_FOUND.value} try: order.confirm() except models.Order.NotPaidError: return {'outcome': self.OutcomeEnum.NOT_PAID.value} except models.Order.AlreadyFulfilledError: return {'outcome': self.OutcomeEnum.ALREADY_FULFILLED.value} except models.Order.TicketAlreadyExistsError: return {'outcome': self.OutcomeEnum.TICKET_ALREADY_EXISTS.value} return {'outcome': self.OutcomeEnum.OK.value} permissions = [] input = { 'order_id': 'ID!', # this is uuid, order's pk is hidden } class OutcomeEnum(enum.Enum): NOT_FOUND = 0 NOT_PAID = 1 OK = 2 ALREADY_FULFILLED = 3 TICKET_ALREADY_EXISTS = 4 Outcome = g.EnumType('RatioConfirmOrderOutcome', OutcomeEnum) # Returning a ticket here is a bad idea: this mutation is public, so we shouldn' expose private objects. # Ratio ticket is currently a private type for usage by internal APIs only. result = g.NN( g.ObjectType( 'RatioConfirmOrderResult', g.fields({ 'outcome': g.NN(Outcome), }), ))
class kkmRegisterCheck(helpers.UnionFieldMixin, helpers.BaseFieldWithInput): def resolve(self, _, info, input): controller: models.Controller = models.Controller.load() result = controller.register_check( email=input['email'], title=input['title'], sum=input['sum'], sign_method_calculation=kkmserver.SignMethodCalculation[ input['sign_method_calculation']], ) # status values: # Ok = 0, Run(Запущено на выполнение) = 1, Error = 2, NotFound(устройство не найдено) = 3, NotRun = 4 if result['Status'] == 0: return RegisterCheckOkResult(url=result['URL']) error = result.get('Error', 'Неизвестная ошибка') return kocherga.django.errors.GenericError( f"{error} (status: {result['Status']})") permissions = [user_perm('kkm.kkmserver')] input = { 'email': str, 'title': str, 'sum': int, 'sign_method_calculation': g.NN(types.KkmSignMethodCalculation), } OkResultType = g.ObjectType( 'KkmRegisterCheckOkResult', g.fields({ 'url': str, }), ) result_types = { RegisterCheckOkResult: OkResultType, kocherga.django.errors.GenericError: kocherga.django.schema.types.GenericError, }
def build_WagtailImage(): def resolve_rendition(obj, info, spec): return obj.get_rendition(spec) return g.ObjectType( 'WagtailImage', lambda: g.fields({ 'id': 'ID!', 'url': str, 'width': int, 'height': int, 'rendition': g.Field( g.NN(WagtailImageRendition), args=g.arguments({'spec': str}), resolve=resolve_rendition, ), }), )
def block_to_types( t: Tuple[str, wagtail.core.blocks.Block], types_for_page_chooser: Dict[str, g.ObjectType] = {}, rewrite_name=True, ): """Converts wagtail block tuple to the list of graphql types. Tuple should look like `('foo_block', blocks.CharBlock())`. This function returns a list of types because complex wagtail blocks can include StreamBlocks which can define new graphql types implementing WagtailBlock interface. """ (name, block_type) = t # false `rewrite_name` is useful in recursion from block_to_gfield if rewrite_name: name = wagtail_to_graphql_block_name(name) extra_types = [] def registrator(t: g.ObjectType): extra_types.append(t) value_field = block_to_gfield(name, block_type, registrator, types_for_page_chooser) return [ g.ObjectType( name, interfaces=[types.WagtailBlock], fields=g.fields({ 'id': 'ID!', 'value': value_field }), ), *extra_types, ]
EventsPrototype = g.ObjectType( 'EventsPrototype', lambda: g.fields({ 'id': g.Field(g.NN(g.ID), resolve=lambda obj, info: obj.prototype_id), **django_utils.model_fields( models.EventPrototype, [ 'title', 'summary', 'description', 'location', 'timing_description_override', 'active', 'weekday', 'hour', 'minute', 'length', ], ), 'project': g.Field(ProjectPage), 'tags': tags_field(), 'image': wagtail_utils.image_rendition_field(models.EventPrototype, 'image'), 'suggested_dates': suggested_dates_field(), 'instances': instances_field(), 'vk_group': vk_group_field(), 'timepad_category': timepad_category_field(), }), )
from kocherga.graphql import g from kocherga.auth.schema.types import AuthUser EventsTicket = g.ObjectType( 'EventsTicket', g.fields({ 'id': 'ID!', 'status': str, 'user': g.NN(AuthUser) }))
[permissions.manage_events]), 'timing_description_override': helpers.field_with_permissions(g.NN(g.String), [permissions.manage_events]), 'tickets': django_utils.related_field( models.Event, 'tickets', item_type=EventsTicket, permissions=[permissions.manage_events], ), 'feedbacks': django_utils.related_field( models.Event, 'feedbacks', item_type=EventsFeedback, permissions=[permissions.manage_events], ), 'youtube_videos': django_utils.related_field( models.Event, 'youtube_videos', item_type=EventsYoutubeVideo, ), }) Event = g.ObjectType('Event', fields=build_event_fields) EventConnection = helpers.ConnectionType(Event)
from typing import Optional from kocherga.graphql import g, helpers from .event import Event MyEventsTicket = g.ObjectType( 'MyEventsTicket', g.fields({ 'id': g.Field(g.NN(g.ID), resolve=lambda obj, info: obj.event.uuid), 'event': g.NN(Event), 'status': str, 'created': Optional[str], 'zoom_link': Optional[str], }), ) MyEventsTicketConnection = helpers.ConnectionType(MyEventsTicket)
'ValidationError', fields={ 'errors': g.Field( g.NNList(ValidationErrorItem), resolve=resolve_errors, ), }, ) ValidationError = build_ValidationError() GenericError = g.ObjectType( 'GenericError', fields=g.fields({ 'message': str, }), ) def settings_fields(): # TODO - do we actually need such strict permissions? Maybe these settings should simply be public. # TODO - manage_events permission for telegram_images & other fields seems wrong and arbitrary. from kocherga.events.permissions import manage_events from kocherga.telegram.permissions import view_all_telegram_chats from kocherga.telegram.schema import types as telegram_types from kocherga.wagtail.schema import types as wagtail_types return g.fields({ **{ f: field_with_permissions(g.NN(wagtail_types.WagtailCollection),
from typing import Optional from kocherga.graphql import g, helpers from kocherga.graphql.permissions import authenticated from .. import models c = helpers.Collection() MyEmailSubscriptionInterest = g.ObjectType( 'MyEmailSubscriptionInterest', g.fields({ 'id': 'ID!', 'name': str, 'subscribed': Optional[bool] }), ) MyEmailSubscription = g.ObjectType( 'MyEmailSubscription', g.fields({ 'status': str, 'interests': g.List(g.NN(MyEmailSubscriptionInterest)) }), ) @c.class_field class email_subscription(helpers.BaseField): def resolve(self, _, info): subscription = models.MailchimpMember.get_from_mailchimp( info.context.user.email)
from kocherga.graphql import g, django_utils from kocherga.wagtail import graphql_utils as wagtail_utils from kocherga.wagtail.schema.types import WagtailPage from .. import models FaqEntry = g.ObjectType( 'FaqEntry', g.fields({ **django_utils.model_fields(models.Entry, ['id', 'question']), 'answer': wagtail_utils.richtext_field(models.Entry, 'answer'), }), ) FaqPage = g.ObjectType( 'FaqPage', interfaces=[WagtailPage], fields=lambda: g.fields({ **wagtail_utils.basic_fields(), **django_utils.model_fields(models.FAQPage, ['title', 'summary']), 'prev_page': FaqPage, 'next_page': FaqPage, 'entries': django_utils.related_field( models.FAQPage, 'entries', item_type=FaqEntry), 'subpages': g.NNList(FaqPage),
from typing import Optional from kocherga.graphql import g, helpers Cm2Order = g.ObjectType( 'Cm2Order', fields=lambda: g.fields({ 'id': 'ID!', 'start': str, 'end': Optional[str], 'customer': Cm2Customer, 'value': int, }), ) Cm2OrderConnection = helpers.ConnectionType(Cm2Order) def resolve_Cm2Customer_orders(obj, info, **pager): return obj.orders.filter_by_customer_id(obj.pk).relay_page(**pager) Cm2Customer = g.ObjectType( 'Cm2Customer', g.fields({ 'id': 'ID!', 'card_id': int, 'first_name': str,
import logging from asgiref.sync import sync_to_async logger = logging.getLogger(__name__) from kocherga.graphql import g, helpers from kocherga.graphql.permissions import check_permissions from .. import permissions, channels c = helpers.Collection() WatchmenScheduleUpdateNotification = g.ObjectType( 'WatchmenScheduleUpdateNotification', g.fields({'updated': bool})) @c.field def watchmenScheduleUpdates(_): async def subscribe(obj, info): # check permissions await sync_to_async(permissions.manage_watchmen, thread_sensitive=True)(obj, info) async for msg in channels.watchmen_updates_group.subscribe(): logger.info('yielding True') yield True @check_permissions([permissions.manage_watchmen]) def resolve(msg, info): logger.info('returning updated')
class is_paid(helpers.BaseField): def resolve(self, obj: models.Payment, info): return obj.is_paid permissions = [] result = bool @fields.class_field class status(helpers.BaseField): def resolve(self, obj: models.Payment, info): return obj.status.value permissions = [] result = g.NN(g.EnumType('YandexKassaPaymentStatus', models.PaymentStatus)) @fields.class_field class waiting_for_capture(helpers.BaseField): def resolve(self, obj, info): return obj.waiting_for_capture permissions = [] result = bool YandexKassaPayment = g.ObjectType( 'YandexKassaPayment', fields=fields.as_dict(), )
import graphql import kocherga.projects.models import kocherga.wagtail.models from kocherga.error import PublicError from kocherga.graphql import django_utils, g, helpers from kocherga.graphql.basic_types import BasicResult from kocherga.graphql.permissions import authenticated from ... import models, permissions from ..types import Event c = helpers.Collection() EventUpdateResult = g.ObjectType( 'EventUpdateResult', g.fields({ 'ok': Optional[bool], 'event': g.NN(Event) })) @c.class_field class eventCreate(helpers.BaseFieldWithInput): def resolve(self, _, info, input): title = input['title'] start = input['start'] end = input['end'] start = dateutil.parser.isoparse(start) end = dateutil.parser.isoparse(end) params = {
# WagtailPageMixin.resolve must return either PrivatePage or WagtailPageContainer raise Exception("Internal logic error") deprecation_reason = 'Use wagtailPageOrPrivate instead' permissions = [] args = { 'page_id': 'ID', 'path': Optional[str], 'preview_token': Optional[str], } result = types.WagtailPage WagtailPageContainerType = g.ObjectType( 'WagtailPageContainer', fields=g.fields({ 'page': types.WagtailPage, # yes, nullable }), ) WagtailPagePrivateType = g.ObjectType( 'WagtailPagePrivate', fields=g.fields({ 'message': g.Field(g.NN(g.String), resolve=lambda obj, info: 'private') }), ) # wagtailPageOrPrivate(...) { # ... on WagtailPageContainer { # ...