class Query(graphene.ObjectType): """Allow querying objects.""" allReleases = graphene.List(Release) getReleases = graphene.Field(lambda: graphene.List(Release), name=graphene.String(), id_prefix=graphene.String(), composed_by_bodhi=graphene.Boolean(), state=graphene.String()) getUpdates = graphene.Field(lambda: graphene.List(Update), stable_karma=graphene.Int(), stable_days=graphene.Int(), unstable_karma=graphene.Int(), status=graphene.String(), request=graphene.String(), pushed=graphene.Boolean(), critpath=graphene.Boolean(), date_approved=graphene.String(), alias=graphene.String(), user_id=graphene.Int(), release_name=graphene.String()) getBuildrootOverrides = graphene.Field( lambda: graphene.List(BuildrootOverride), submission_date=graphene.DateTime(), expiration_date=graphene.DateTime(), build_nvr=graphene.String(), submitter_username=graphene.String()) def resolve_allReleases(self, info): """Answer Queries by fetching data from the Schema.""" query = Release.get_query(info) # SQLAlchemy query return query.all() def resolve_getReleases(self, info, **args): """Answer Release queries with a given argument.""" query = Release.get_query(info) id_prefix = args.get("id_prefix") if id_prefix is not None: query = query.filter(ReleaseModel.id_prefix == id_prefix) name = args.get("name") if name is not None: query = query.filter(ReleaseModel.name == name) composed_by_bodhi = args.get("composed_by_bodhi") if composed_by_bodhi is not None: query = query.filter( ReleaseModel.composed_by_bodhi == composed_by_bodhi) state = args.get("state") if state is not None: query = query.filter(ReleaseModel.state == state) return query.all() def resolve_getUpdates(self, info, **args): """Answer Release queries with a given argument.""" query = Update.get_query(info) stable_karma = args.get("stable_karma") if stable_karma is not None: query = query.filter(UpdateModel.stable_karma == stable_karma) stable_days = args.get("stable_days") if stable_days is not None: query = query.filter(UpdateModel.stable_days == stable_days) unstable_karma = args.get("unstable_karma") if unstable_karma is not None: query = query.filter(UpdateModel.unstable_karma == unstable_karma) status = args.get("status") if status is not None: query = query.filter(UpdateModel.status == status) request = args.get("request") if request is not None: query = query.filter(UpdateModel.request == request) pushed = args.get("pushed") if pushed is not None: query = query.filter(UpdateModel.pushed == pushed) critpath = args.get("critpath") if critpath is not None: query = query.filter(UpdateModel.critpath == critpath) date_approved = args.get("date_approved") if date_approved is not None: query = query.filter(UpdateModel.date_approved == date_approved) alias = args.get("alias") if alias is not None: query = query.filter(UpdateModel.alias == alias) user_id = args.get("user_id") if user_id is not None: query = query.filter(UpdateModel.user_id == user_id) release_name = args.get("release_name") if release_name is not None: query = query.join( UpdateModel.release).filter(ReleaseModel.name == release_name) return query.all() def resolve_getBuildrootOverrides(self, info, **args): """Answer Release queries with a given argument.""" query = BuildrootOverride.get_query(info) submission_date = args.get("submission_date") if submission_date is not None: query = query.filter( BuildrootOverrideModel.submission_date == submission_date) expiration_date = args.get("expiration_date") if expiration_date is not None: query = query.filter( BuildrootOverrideModel.expiration_date == expiration_date) build_nvr = args.get("build_nvr") if build_nvr is not None: query = query.join(BuildrootOverrideModel.build).filter( BuildModel.nvr == build_nvr) submitter_username = args.get("submitter_username") if submitter_username is not None: query = query.join(BuildrootOverrideModel.submitter).filter( UserModel.name == submitter_username) return query.all()
class Query(graphene.AbstractType): all_services = graphene.List(ServiceType, kind=graphene.String()) all_locations = graphene.List(LocationType, id=graphene.Int()) all_items = graphene.List(MissingItemType, id=graphene.Int()) all_offices = graphene.List(OfficeType, id=graphene.Int()) all_querys = graphene.List(SQLQueryType, id=graphene.Int()) service = graphene.Field(ServiceType, id=graphene.Int()) location = graphene.Field(LocationType, id=graphene.Int()) item = graphene.Field(MissingItemType, id=graphene.Int()) office = graphene.Field(OfficeType, id=graphene.Int()) query = graphene.Field(SQLQueryType, id=graphene.Int()) def resolve_all_services(self, info, **kwargs): kind = kwargs.get('kind') if kind is not None: return Service.objects.all().filter(kind=kind) return Service.objects.all() def resolve_all_locations(self, info, **kwargs): id = kwargs.get('id') source = None if id is not None: source = Service.objects.get(pk=id) if source is not None: return Location.objects.all().filter(Service=source) return Location.objects.all() def resolve_all_items(self, info, **kwargs): id = kwargs.get('id') source = None if id is not None: source = Service.objects.get(pk=id) if source is not None: return MissingItem.objects.all().filter(Service=source) return MissingItem.objects.all() def resolve_all_offices(self, info, **kwargs): id = kwargs.get('id') source = None if id is not None: source = Service.objects.get(pk=id) if source is not None: return Office.objects.all().filter(Service=source) return Office.objects.all() def resolve_all_querys(self, info, **kwargs): id = kwargs.get('id') source = None if id is not None: source = Service.objects.get(pk=id) if source is not None: return SQLQuery.objects.all().filter(Service=source) return SQLQuery.objects.all() def resolve_service(self, info, **kwargs): id = kwargs.get('id') if id is not None: return Service.objects.get(pk=id) return None def resolve_location(self, info, **kwargs): id = kwargs.get('id') if id is not None: return Location.objects.get(pk=id) return None def resolve_item(self, info, **kwargs): id = kwargs.get('id') if id is not None: return MissingItem.objects.get(pk=id) return None def resolve_office(self, info, **kwargs): id = kwargs.get('id') if id is not None: return Office.objects.get(pk=id) return None def resolve_query(self, info, **kwargs): id = kwargs.get('id') if id is not None: return SQLQuery.objects.get(pk=id) return None
class ProductStockFilterInput(graphene.InputObjectType): warehouse_ids = graphene.List(graphene.NonNull(graphene.ID), required=False) quantity = graphene.Field(IntRangeInput, required=False)
class ProductVariant(CountableDjangoObjectType, MetadataObjectType): quantity = graphene.Int( required=True, description="Quantity of a product in the store's possession, " "including the allocated stock that is waiting for shipment.", ) stock_quantity = graphene.Int( required=True, description="Quantity of a product available for sale." ) price_override = graphene.Field( Money, description=( "Override the base price of a product if necessary. A value of `null` " "indicates that the default product price is used." ), ) price = graphene.Field( Money, description="Price of the product variant.", deprecation_reason=( "DEPRECATED: Will be removed in Saleor 2.10, " "has been replaced by 'pricing.priceUndiscounted'" ), ) availability = graphene.Field( VariantPricingInfo, description=( "Informs about variant's availability in the storefront, current price and " "discounted price." ), deprecation_reason=( "DEPRECATED: Will be removed in Saleor 2.10, has been renamed to `pricing`." ), ) pricing = graphene.Field( VariantPricingInfo, description=( "Lists the storefront variant's pricing, the current price and discounts, " "only meant for displaying." ), ) is_available = graphene.Boolean( description="Whether the variant is in stock and visible or not." ) attributes = gql_optimizer.field( graphene.List( graphene.NonNull(SelectedAttribute), required=True, description="List of attributes assigned to this variant.", ) ) cost_price = graphene.Field(Money, description="Cost price of the variant.") margin = graphene.Int(description="Gross margin percentage value.") quantity_ordered = graphene.Int(description="Total quantity ordered.") revenue = graphene.Field( TaxedMoney, period=graphene.Argument(ReportingPeriod), description=( "Total revenue generated by a variant in given period of time. Note: this " "field should be queried using `reportProductSales` query as it uses " "optimizations suitable for such calculations." ), ) images = gql_optimizer.field( graphene.List( lambda: ProductImage, description="List of images for the product variant." ), model_field="images", ) translation = graphene.Field( ProductVariantTranslation, language_code=graphene.Argument( LanguageCodeEnum, description="A language code to return the translation for.", required=True, ), description=( "Returns translated Product Variant fields " "for the given language code." ), resolver=resolve_translation, ) digital_content = gql_optimizer.field( graphene.Field( DigitalContent, description="Digital content for the product variant." ), model_field="digital_content", ) class Meta: description = ( "Represents a version of a product such as different size or color." ) only_fields = [ "id", "name", "product", "quantity_allocated", "sku", "track_inventory", "weight", ] interfaces = [relay.Node] model = models.ProductVariant @staticmethod @permission_required("product.manage_products") def resolve_digital_content(root: models.ProductVariant, *_args): return getattr(root, "digital_content", None) @staticmethod def resolve_stock_quantity(root: models.ProductVariant, _info): exact_quantity_available = root.quantity_available return min(exact_quantity_available, settings.MAX_CHECKOUT_LINE_QUANTITY) @staticmethod @gql_optimizer.resolver_hints( prefetch_related=["attributes__values", "attributes__assignment__attribute"] ) def resolve_attributes(root: models.ProductVariant, info): return resolve_attribute_list(root, user=info.context.user) @staticmethod @permission_required("product.manage_products") def resolve_margin(root: models.ProductVariant, *_args): return get_margin_for_variant(root) @staticmethod @permission_required("product.manage_products") def resolve_cost_price(root: models.ProductVariant, *_args): return root.cost_price @staticmethod def resolve_price(root: models.ProductVariant, *_args): return ( root.price_override if root.price_override is not None else root.product.price ) @staticmethod @gql_optimizer.resolver_hints( prefetch_related=("product",), only=["price_override_amount", "currency"] ) def resolve_pricing(root: models.ProductVariant, info): context = info.context availability = get_variant_availability( root, context.discounts, context.country, context.currency, extensions=context.extensions, ) return VariantPricingInfo(**availability._asdict()) resolve_availability = resolve_pricing @staticmethod def resolve_is_available(root: models.ProductVariant, _info): return root.is_available @staticmethod @permission_required("product.manage_products") def resolve_price_override(root: models.ProductVariant, *_args): return root.price_override @staticmethod @permission_required("product.manage_products") def resolve_quantity(root: models.ProductVariant, *_args): return root.quantity @staticmethod @permission_required(["order.manage_orders", "product.manage_products"]) def resolve_quantity_ordered(root: models.ProductVariant, *_args): # This field is added through annotation when using the # `resolve_report_product_sales` resolver. return getattr(root, "quantity_ordered", None) @staticmethod @permission_required(["order.manage_orders", "product.manage_products"]) def resolve_quantity_allocated(root: models.ProductVariant, *_args): return root.quantity_allocated @staticmethod @permission_required(["order.manage_orders", "product.manage_products"]) def resolve_revenue(root: models.ProductVariant, *_args, period): start_date = reporting_period_to_date(period) return calculate_revenue_for_variant(root, start_date) @staticmethod def resolve_images(root: models.ProductVariant, *_args): return root.images.all() @classmethod def get_node(cls, info, id): user = info.context.user visible_products = models.Product.objects.visible_to_user(user).values_list( "pk", flat=True ) qs = cls._meta.model.objects.filter(product__id__in=visible_products) return cls.maybe_optimize(info, qs, id) @staticmethod @permission_required("product.manage_products") def resolve_private_meta(root, _info): return resolve_private_meta(root, _info) @staticmethod def resolve_meta(root, _info): return resolve_meta(root, _info) @staticmethod def __resolve_reference(root, _info, **_kwargs): return graphene.Node.get_node_from_global_id(_info, root.id)
class ProductType(CountableDjangoObjectType, MetadataObjectType): products = gql_optimizer.field( PrefetchingConnectionField( Product, description="List of products of this type." ), prefetch_related=prefetch_products, ) tax_rate = TaxRateType(description="A type of tax rate.") tax_type = graphene.Field( TaxType, description="A type of tax. Assigned by enabled tax gateway" ) variant_attributes = graphene.List( Attribute, description="Variant attributes of that product type." ) product_attributes = graphene.List( Attribute, description="Product attributes of that product type." ) available_attributes = gql_optimizer.field( FilterInputConnectionField(Attribute, filter=AttributeFilterInput()) ) class Meta: description = ( "Represents a type of product. It defines what attributes are available to " "products of this type." ) interfaces = [relay.Node] model = models.ProductType only_fields = [ "has_variants", "id", "is_digital", "is_shipping_required", "name", "weight", "tax_type", ] @staticmethod def resolve_tax_type(root: models.ProductType, info): tax_data = info.context.extensions.get_tax_code_from_object_meta(root) return TaxType(tax_code=tax_data.code, description=tax_data.description) @staticmethod def resolve_tax_rate(root: models.ProductType, info, **_kwargs): # FIXME this resolver should be dropped after we drop tax_rate from API if not hasattr(root, "meta"): return None tax = root.meta.get("taxes", {}).get("vatlayer", {}) return tax.get("code") @staticmethod @gql_optimizer.resolver_hints( prefetch_related="product_attributes__attributeproduct" ) def resolve_product_attributes(root: models.ProductType, *_args, **_kwargs): return root.product_attributes.product_attributes_sorted().all() @staticmethod @gql_optimizer.resolver_hints( prefetch_related="variant_attributes__attributevariant" ) def resolve_variant_attributes(root: models.ProductType, *_args, **_kwargs): return root.variant_attributes.variant_attributes_sorted().all() @staticmethod def resolve_products(root: models.ProductType, info, **_kwargs): if hasattr(root, "prefetched_products"): return root.prefetched_products qs = root.products.visible_to_user(info.context.user) return gql_optimizer.query(qs, info) @staticmethod @permission_required("product.manage_products") def resolve_available_attributes(root: models.ProductType, info, **kwargs): qs = models.Attribute.objects.get_unassigned_attributes(root.pk) return resolve_attributes(info, qs=qs, **kwargs) @staticmethod @permission_required("account.manage_products") def resolve_private_meta(root, _info): return resolve_private_meta(root, _info) @staticmethod def resolve_meta(root, _info): return resolve_meta(root, _info) @staticmethod def __resolve_reference(root, _info, **_kwargs): return graphene.Node.get_node_from_global_id(_info, root.id)
class BillNode(OCDBaseNode): legislative_session = graphene.Field(LegislativeSessionNode) identifier = graphene.String() title = graphene.String() from_organization = graphene.Field(OrganizationNode) classification = graphene.List(graphene.String) subject = graphene.List(graphene.String) # related fields abstracts = graphene.List(BillAbstractNode) other_titles = graphene.List(BillTitleNode) other_identifiers = graphene.List(BillIdentifierNode) actions = graphene.List(BillActionNode) sponsorships = graphene.List(BillSponsorshipNode) related_bills = graphene.List(RelatedBillNode) documents = graphene.List(BillDocumentNode) versions = graphene.List(BillDocumentNode) sources = graphene.List(LinkNode) votes = DjangoConnectionField("graphapi.legislative.VoteConnection") # extra fields openstates_url = graphene.String() def resolve_abstracts(self, info): return self.abstracts.all() def resolve_other_titles(self, info): return self.other_titles.all() def resolve_other_identifiers(self, info): return self.other_identifiers.all() def resolve_actions(self, info): if "actions" not in getattr(self, "_prefetched_objects_cache", []): return optimize( self.actions.all(), info, [ ( ".relatedEntities", Prefetch( "related_entities", BillActionRelatedEntity.objects.all().select_related( "organization", "person" ), ), ) ], [".organization"], ) else: return self.actions.all() def resolve_sponsorships(self, info): return self.sponsorships.all() def resolve_documents(self, info): if "documents" not in getattr(self, "_prefetched_objects_cache", []): return optimize(self.documents.all(), info, [".links"]) else: return self.documents.all() def resolve_versions(self, info): if "versions" not in getattr(self, "_prefetched_objects_cache", []): return optimize(self.versions.all(), info, [".links"]) else: return self.versions.all() def resolve_sources(self, info): return self.sources.all() def resolve_votes(self, info, first=None, last=None, before=None, after=None): if "votes" not in getattr(self, "_prefetched_objects_cache", []): return optimize( self.votes.all(), info, [ ".counts", ( ".votes", Prefetch( "votes", PersonVote.objects.all().select_related("voter") ), ), ], ) else: return self.votes.all() def resolve_related_bills(self, info): if "related_bills" not in getattr(self, "_prefetched_objects_cache", []): return optimize(self.related_bills.all(), info, None, [".relatedBill"]) else: return self.related_bills.all() def resolve_openstates_url(self, info): session = self.legislative_session abbr = session.jurisdiction_id.split("/")[-2].split(":")[1] identifier = self.identifier.replace(" ", "") return f"https://openstates.org/{abbr}/bills/{session.identifier}/{identifier}"
class GraphenePartition(graphene.ObjectType): name = graphene.NonNull(graphene.String) partition_set_name = graphene.NonNull(graphene.String) solid_selection = graphene.List(graphene.NonNull(graphene.String)) mode = graphene.NonNull(graphene.String) runConfigOrError = graphene.NonNull(GraphenePartitionRunConfigOrError) tagsOrError = graphene.NonNull(GraphenePartitionTagsOrError) runs = graphene.Field( non_null_list(GraphenePipelineRun), filter=graphene.Argument(GraphenePipelineRunsFilter), cursor=graphene.String(), limit=graphene.Int(), ) status = graphene.Field(GraphenePipelineRunStatus) class Meta: name = "Partition" def __init__(self, external_repository_handle, external_partition_set, partition_name): self._external_repository_handle = check.inst_param( external_repository_handle, "external_respository_handle", RepositoryHandle) self._external_partition_set = check.inst_param( external_partition_set, "external_partition_set", ExternalPartitionSet) self._partition_name = check.str_param(partition_name, "partition_name") super().__init__( name=partition_name, partition_set_name=external_partition_set.name, solid_selection=external_partition_set.solid_selection, mode=external_partition_set.mode, ) def resolve_runConfigOrError(self, graphene_info): return get_partition_config( graphene_info, self._external_repository_handle, self._external_partition_set.name, self._partition_name, ) def resolve_tagsOrError(self, graphene_info): return get_partition_tags( graphene_info, self._external_repository_handle, self._external_partition_set.name, self._partition_name, ) def resolve_runs(self, graphene_info, **kwargs): filters = kwargs.get("filter") partition_tags = { PARTITION_SET_TAG: self._external_partition_set.name, PARTITION_NAME_TAG: self._partition_name, } if filters is not None: filters = filters.to_selector() runs_filter = PipelineRunsFilter( run_ids=filters.run_ids, pipeline_name=filters.pipeline_name, statuses=filters.statuses, tags=merge_dicts(filters.tags, partition_tags), ) else: runs_filter = PipelineRunsFilter(tags=partition_tags) return get_runs(graphene_info, runs_filter, cursor=kwargs.get("cursor"), limit=kwargs.get("limit"))
class SetPassword(CreateToken): user = graphene.Field(User, description="A user instance with new password.") account_errors = graphene.List( graphene.NonNull(AccountError), description="List of errors that occurred executing the mutation.", ) class Arguments: token = graphene.String( description="A one-time token required to set the password.", required=True) class Meta: description = ("Sets the user's password from the token sent by email " "using the RequestPasswordReset mutation.") @classmethod def mutate(cls, root, info, **data): email = data["email"] password = data["password"] token = data["token"] try: cls._set_password_for_user(email, password, token) except ValidationError as e: errors = validation_error_to_error_type(e) return cls.handle_typed_errors(errors) return super().mutate(root, info, **data) @classmethod def _set_password_for_user(cls, email, password, token): try: user = models.User.objects.get(email=email) except ObjectDoesNotExist: raise ValidationError({ "email": ValidationError("User doesn't exist", code=AccountErrorCode.NOT_FOUND) }) if not default_token_generator.check_token(user, token): raise ValidationError({ "token": ValidationError(INVALID_TOKEN, code=AccountErrorCode.INVALID) }) try: password_validation.validate_password(password, user) except ValidationError as error: raise ValidationError({"password": error}) user.set_password(password) user.save(update_fields=["password"]) account_events.customer_password_reset_event(user=user) @classmethod def handle_typed_errors(cls, errors: list): account_errors = [ AccountError(field=e.field, message=e.message, code=code) for e, code, _params in errors ] return cls(errors=[e[0] for e in errors], account_errors=account_errors)
class Arguments: ids = graphene.List(graphene.ID, required=True, description='List of page IDs to delete.')
def _get_fields(model, only_fields, exclude_fields, required_fields): fields = [ (field.name, field) for field in sorted(list(model._meta.fields + model._meta.many_to_many)) ] # Only add the field as input field if the foreign key (reverse side) # can be set to null, otherwise updates won't work. fields.extend([ (field.related_name or field.name + "_set", field) for field in sorted( list(model._meta.related_objects), key=lambda field: field.name, ) if not isinstance(field, ManyToOneRel) or field.remote_field.null ], ) ret = collections.OrderedDict() for name, field in fields: if ((only_fields and name not in only_fields) or name in exclude_fields or str(name).endswith("+") or name in ["created_at", "updated_at", "archived_at"]): continue if name == "id": f = graphene.ID(description="The ID of the object.", ) elif isinstance(field, models.FileField): f = UploadType(description=field.help_text, ) elif isinstance(field, models.BooleanField): f = graphene.Boolean(description=field.help_text, ) elif isinstance(field, (models.ForeignKey, models.OneToOneField)): f = graphene.ID(description=field.help_text, ) elif isinstance(field, models.ManyToManyField): f = graphene.List( graphene.ID, description=field.help_text, ) elif isinstance(field, (ManyToOneRel, ManyToManyRel)): reverse_rel_include = graphene_django_plus_settings.MUTATIONS_INCLUDE_REVERSE_RELATIONS # Checking whether it was globally configured to not include reverse relations if isinstance(field, ManyToOneRel ) and not reverse_rel_include and not only_fields: continue f = graphene.List( graphene.ID, description="Set list of {0}".format( field.related_model._meta.verbose_name_plural, ), ) else: f = convert_django_field_with_choices(field, _registry) if required_fields is not None: f.kwargs["required"] = name in required_fields else: if isinstance(field, (ManyToOneRel, ManyToManyRel)): f.kwargs["required"] = not field.null else: f.kwargs[ "required"] = not field.blank and field.default is NOT_PROVIDED if getattr(field, "choices", None): items = field.choices if isinstance(items, dict): items = items.items() choices = [] for (original_v, label), (n, value, desc) in zip(items, get_choices(items)): choices.append({ "label": label, "name": n, "value": value, }) else: choices = None s = get_field_schema(field) s.update({ "name": name, # FIXME: Get verbose_name and help_text for m2m "label": getattr(field, "verbose_name", None), "help_text": getattr(field, "help_text", None), "max_length": getattr(field, "max_length", None), "choices": choices, }) ret[name] = { "field": f, "schema": s, } return ret
class BaseMutation(ClientIDMutation): """Base mutation enchanced with permission checking and relay id handling.""" class Meta: abstract = True #: A list of errors that happened during the mutation errors = graphene.List( graphene.NonNull(MutationErrorType), description=_( "List of errors that occurred while executing the mutation."), ) @classmethod def __init_subclass_with_meta__( cls, permissions=None, permissions_any=True, allow_unauthenticated=False, input_schema=None, _meta=None, **kwargs, ): if not _meta: _meta = BaseMutationOptions(cls) _meta.permissions = permissions or [] _meta.permissions_any = permissions_any _meta.allow_unauthenticated = allow_unauthenticated _meta.input_schema = input_schema or {} super().__init_subclass_with_meta__(_meta=_meta, **kwargs) iname = cls.Input._meta.name input_schema_registry[iname] = { "input_object": iname, "fields": list(_meta.input_schema.values()), } @classmethod def get_node(cls, info, node_id, field="id", only_type=None): """Get the node object given a relay global id.""" if not node_id: return None try: node = get_node(node_id, only_type) except (AssertionError, GraphQLError) as e: raise ValidationError({field: str(e)}) else: if node is None: # pragma: no cover raise ValidationError( {field: "Couldn't resolve to a node: {}".format(node_id)}) return node @classmethod def get_nodes(cls, ids, field, only_type=None): """Get a list of node objects given a list of relay global ids.""" try: instances = get_nodes(ids, only_type) except GraphQLError as e: raise ValidationError({field: str(e)}) return instances @classmethod def check_permissions(cls, user): """Check permissions for the given user. Subclasses can override this to avoid the permission checking or extending it. Remember to call `super()` in the later case. """ if not cls._meta.allow_unauthenticated and not check_authenticated( user): return False if not cls._meta.permissions: return True return check_perms(user, cls._meta.permissions, any_perm=cls._meta.permissions_any) @classmethod def mutate_and_get_payload(cls, root, info, **data): """Mutate checking permissions. We override the default graphene's method to call :meth:`.check_permissions` and populate :attr:`.errors` in case of errors automatically. The mutation itself should be defined in :meth:`.perform_mutation`. """ if not cls.check_permissions(info.context.user): raise PermissionDenied() try: response = cls.perform_mutation(root, info, **data) if response.errors is None: response.errors = [] return response except ValidationError as e: errors = _get_validation_errors(e) return cls(errors=errors) @classmethod def perform_mutation(cls, root, info, **data): """Perform the mutation. This should be implemented in subclasses to perform the mutation. """ raise NotImplementedError
class GrapheneSensorMetadata(graphene.ObjectType): assetKeys = graphene.List(graphene.NonNull(GrapheneAssetKey)) class Meta: name = "SensorMetadata"
class GiftCardBulkCreate(BaseMutation): count = graphene.Int( required=True, default_value=0, description="Returns how many objects were created.", ) gift_cards = graphene.List( graphene.NonNull(GiftCard), required=True, default_value=[], description="List of created gift cards.", ) class Arguments: input = GiftCardBulkCreateInput( required=True, description="Fields required to create gift cards." ) class Meta: description = f"{ADDED_IN_31} Create gift cards. {PREVIEW_FEATURE}" model = models.GiftCard permissions = (GiftcardPermissions.MANAGE_GIFT_CARD,) error_type_class = GiftCardError @classmethod @traced_atomic_transaction() def perform_mutation(cls, _root, info, **data): input_data = data["input"] cls.clean_count_value(input_data) cls.clean_expiry_date(input_data) cls.clean_balance(input_data) GiftCardCreate.set_created_by_user(input_data, info) tags = input_data.pop("tags", None) instances = cls.create_instances(input_data, info) if tags: cls.assign_gift_card_tags(instances, tags) return cls(count=len(instances), gift_cards=instances) @staticmethod def clean_count_value(input_data): if not input_data["count"] > 0: raise ValidationError( { "count": ValidationError( "Count value must be greater than 0.", code=GiftCardErrorCode.INVALID.value, ) } ) @staticmethod def clean_expiry_date(input_data): expiry_date = input_data.get("expiry_date") if expiry_date and not is_date_in_future(expiry_date): raise ValidationError( { "expiry_date": ValidationError( "Expiry date cannot be in the past.", code=GiftCardErrorCode.INVALID.value, ) } ) @staticmethod def clean_balance(cleaned_input): balance = cleaned_input["balance"] amount = balance["amount"] currency = balance["currency"] try: validate_price_precision(amount, currency) except ValidationError as error: error.code = GiftCardErrorCode.INVALID.value raise ValidationError({"balance": error}) if not amount > 0: raise ValidationError( { "balance": ValidationError( "Balance amount have to be greater than 0.", code=GiftCardErrorCode.INVALID.value, ) } ) cleaned_input["currency"] = currency cleaned_input["current_balance_amount"] = amount cleaned_input["initial_balance_amount"] = amount @staticmethod def create_instances(cleaned_input, info): count = cleaned_input.pop("count") balance = cleaned_input.pop("balance") gift_cards = models.GiftCard.objects.bulk_create( [ models.GiftCard(code=generate_promo_code(), **cleaned_input) for _ in range(count) ] ) events.gift_cards_issued_event( gift_cards, info.context.user, info.context.app, balance ) return gift_cards @staticmethod def assign_gift_card_tags( instances: Iterable[models.GiftCard], tags_values: Iterable[str] ): tags = {tag.lower() for tag in tags_values} tags_instances = models.GiftCardTag.objects.filter(name__in=tags) tags_to_create = tags - set(tags_instances.values_list("name", flat=True)) models.GiftCardTag.objects.bulk_create( [models.GiftCardTag(name=tag) for tag in tags_to_create] ) for tag_instance in tags_instances.iterator(): tag_instance.gift_cards.set(instances)
class Arguments: ids = graphene.List( graphene.ID, required=True, description="List of gift card IDs to deactivate.", )
class Arguments: ids = graphene.List(graphene.ID, required=True, description="List of orders IDs to cancel.")
class Query(graphene.ObjectType): users = graphene.List(Basic) def resolve_users(self, info): return BasicInformation.objects.all()
class Query(graphene.ObjectType): messages = graphene.List(SupportMessageType) def resolve_messages(self, info): return SupportMessage.objects.all()
class PersonNode(OCDBaseNode): name = graphene.String() sort_name = graphene.String() family_name = graphene.String() given_name = graphene.String() image = graphene.String() # not used: gender, summary, national_identity, biography birth_date = graphene.String() death_date = graphene.String() # related objects identifiers = graphene.List(IdentifierNode) other_names = graphene.List(NameNode) links = graphene.List(LinkNode) sources = graphene.List(LinkNode) contact_details = graphene.List(ContactDetailNode) # special attributes current_memberships = graphene.List( "graphapi.core.MembershipNode", classification=graphene.List(graphene.String) ) old_memberships = graphene.List( "graphapi.core.MembershipNode", classification=graphene.List(graphene.String) ) votes = graphene.List("graphapi.legislative.BillVoteNode") def resolve_identifiers(self, info): return self.identifiers.all() def resolve_other_names(self, info): return self.other_names.all() def resolve_links(self, info): return self.links.all() def resolve_sources(self, info): return self.sources.all() def resolve_contact_details(self, info): return self.contact_details.all() def resolve_current_memberships(self, info, classification=None): if hasattr(self, "current_memberships"): if classification: return [ m for m in self.current_memberships if m.organization.classification in classification ] return self.current_memberships else: return _membership_filter( self.memberships, info, classification, current=True ) def resolve_old_memberships(self, info, classification=None): if hasattr(self, "old_memberships"): if classification: return [ m for m in self.old_memberships if m.organization.classification in classification ] return self.old_memberships else: return _membership_filter( self.memberships, info, classification, current=False ) def resolve_votes(self, info): return self.votes.all()
class GraphenePartitionSet(graphene.ObjectType): id = graphene.NonNull(graphene.ID) name = graphene.NonNull(graphene.String) pipeline_name = graphene.NonNull(graphene.String) solid_selection = graphene.List(graphene.NonNull(graphene.String)) mode = graphene.NonNull(graphene.String) partitionsOrError = graphene.Field( graphene.NonNull(GraphenePartitionsOrError), cursor=graphene.String(), limit=graphene.Int(), reverse=graphene.Boolean(), ) partition = graphene.Field(GraphenePartition, partition_name=graphene.NonNull( graphene.String)) partitionStatusesOrError = graphene.NonNull( GraphenePartitionStatusesOrError) repositoryOrigin = graphene.NonNull(GrapheneRepositoryOrigin) class Meta: name = "PartitionSet" def __init__(self, external_repository_handle, external_partition_set): self._external_repository_handle = check.inst_param( external_repository_handle, "external_respository_handle", RepositoryHandle) self._external_partition_set = check.inst_param( external_partition_set, "external_partition_set", ExternalPartitionSet) super().__init__( name=external_partition_set.name, pipeline_name=external_partition_set.pipeline_name, solid_selection=external_partition_set.solid_selection, mode=external_partition_set.mode, ) def resolve_id(self, _graphene_info): return self._external_partition_set.get_external_origin_id() def resolve_partitionsOrError(self, graphene_info, **kwargs): return get_partitions( graphene_info, self._external_repository_handle, self._external_partition_set, cursor=kwargs.get("cursor"), limit=kwargs.get("limit"), reverse=kwargs.get("reverse"), ) def resolve_partition(self, graphene_info, partition_name): return get_partition_by_name( graphene_info, self._external_repository_handle, self._external_partition_set, partition_name, ) def resolve_partitionStatusesOrError(self, graphene_info): return get_partition_set_partition_statuses( graphene_info, self._external_repository_handle, self._external_partition_set.name) def resolve_repositoryOrigin(self, _): origin = self._external_partition_set.get_external_origin( ).external_repository_origin return GrapheneRepositoryOrigin(origin)
class Arguments: attributes = graphene.Argument(graphene.List(AttributeValueInput)) category_id = graphene.ID()
class AppQueries(graphene.ObjectType): apps_installations = graphene.List( graphene.NonNull(AppInstallation), description="List of all apps installations", required=True, ) apps = FilterInputConnectionField( App, filter=AppFilterInput(description="Filtering options for apps."), sort_by=AppSortingInput(description="Sort apps."), description="List of the apps.", ) app = graphene.Field( App, id=graphene.Argument(graphene.ID, description="ID of the app.", required=False), description=( "Look up an app by ID. " "If ID is not provided, return the currently authenticated app."), ) app_extensions = FilterInputConnectionField( AppExtension, filter=AppExtensionFilterInput( description="Filtering options for apps extensions."), description=f"{ADDED_IN_31} List of all extensions", ) app_extension = graphene.Field( AppExtension, id=graphene.Argument(graphene.ID, description="ID of the app extension.", required=True), description=f"{ADDED_IN_31} Look up an app extension by ID.", ) @permission_required(AppPermission.MANAGE_APPS) def resolve_apps_installations(self, info, **kwargs): return resolve_apps_installations(info, **kwargs) @permission_required(AppPermission.MANAGE_APPS) def resolve_apps(self, info, **kwargs): return resolve_apps(info, **kwargs) def resolve_app(self, info, id=None): app = info.context.app if not id and app: return app return resolve_app(info, id) @staff_member_or_app_required def resolve_app_extensions(self, info, **kwargs): return resolve_app_extensions(info) @staff_member_or_app_required def resolve_app_extension(self, info, id): def app_is_active(app_extension): def is_active(app): if app.is_active: return app_extension return None if not app_extension: return None return (AppByIdLoader(info.context).load( app_extension.app_id).then(is_active)) _, id = from_global_id_or_error(id, "AppExtension") return AppExtensionByIdLoader(info.context).load( int(id)).then(app_is_active)
class Arguments: product_type_id = graphene.ID() category_id = graphene.ID() attributes = graphene.Argument(graphene.List(AttributeValueInput))
class Product(CountableDjangoObjectType, MetadataObjectType): url = graphene.String( description="The storefront URL for the product.", required=True ) thumbnail = graphene.Field( Image, description="The main thumbnail for a product.", size=graphene.Argument(graphene.Int, description="Size of thumbnail."), ) availability = graphene.Field( ProductPricingInfo, description=( "Informs about product's availability in the storefront, current price and " "discounts." ), deprecation_reason=( "DEPRECATED: Will be removed in Saleor 2.10, Has been renamed to `pricing`." ), ) pricing = graphene.Field( ProductPricingInfo, description=( "Lists the storefront product's pricing, the current price and discounts, " "only meant for displaying." ), ) is_available = graphene.Boolean( description="Whether the product is in stock and visible or not." ) base_price = graphene.Field(Money, description="The product's default base price.") price = graphene.Field( Money, description="The product's default base price.", deprecation_reason=( "DEPRECATED: Will be removed in Saleor 2.10, has been replaced by " "`basePrice`" ), ) minimal_variant_price = graphene.Field( Money, description="The price of the cheapest variant (including discounts)." ) tax_type = graphene.Field( TaxType, description="A type of tax. Assigned by enabled tax gateway" ) attributes = graphene.List( graphene.NonNull(SelectedAttribute), required=True, description="List of attributes assigned to this product.", ) purchase_cost = graphene.Field(MoneyRange) margin = graphene.Field(Margin) image_by_id = graphene.Field( lambda: ProductImage, id=graphene.Argument(graphene.ID, description="ID of a product image."), description="Get a single product image by ID.", ) variants = gql_optimizer.field( graphene.List(ProductVariant, description="List of variants for the product."), model_field="variants", ) images = gql_optimizer.field( graphene.List( lambda: ProductImage, description="List of images for the product." ), model_field="images", ) collections = gql_optimizer.field( graphene.List( lambda: Collection, description="List of collections for the product." ), model_field="collections", ) translation = graphene.Field( ProductTranslation, language_code=graphene.Argument( LanguageCodeEnum, description="A language code to return the translation for.", required=True, ), description=("Returns translated Product fields for the given language code."), resolver=resolve_translation, ) slug = graphene.String(required=True, description="The slug of a product.") class Meta: description = "Represents an individual item for sale in the storefront." interfaces = [relay.Node] model = models.Product only_fields = [ "category", "charge_taxes", "description", "description_json", "id", "is_published", "name", "product_type", "publication_date", "seo_description", "seo_title", "updated_at", "weight", ] @staticmethod def resolve_tax_type(root: models.Product, info): tax_data = info.context.extensions.get_tax_code_from_object_meta(root) return TaxType(tax_code=tax_data.code, description=tax_data.description) @staticmethod @gql_optimizer.resolver_hints(prefetch_related="images") def resolve_thumbnail(root: models.Product, info, *, size=255): image = root.get_first_image() if image: url = get_product_image_thumbnail(image, size, method="thumbnail") alt = image.alt return Image(alt=alt, url=info.context.build_absolute_uri(url)) return None @staticmethod def resolve_url(root: models.Product, *_args): return root.get_absolute_url() @staticmethod @gql_optimizer.resolver_hints( prefetch_related=("variants", "collections"), only=["publication_date", "charge_taxes", "price_amount", "currency", "meta"], ) def resolve_pricing(root: models.Product, info): context = info.context availability = get_product_availability( root, context.discounts, context.country, context.currency, context.extensions, ) return ProductPricingInfo(**availability._asdict()) resolve_availability = resolve_pricing @staticmethod @gql_optimizer.resolver_hints(prefetch_related=("variants")) def resolve_is_available(root: models.Product, _info): return root.is_available @staticmethod @permission_required("product.manage_products") def resolve_base_price(root: models.Product, _info): return root.price @staticmethod @gql_optimizer.resolver_hints( prefetch_related=("variants", "collections"), only=["publication_date", "charge_taxes", "price_amount", "currency", "meta"], ) def resolve_price(root: models.Product, info): price_range = root.get_price_range(info.context.discounts) price = info.context.extensions.apply_taxes_to_product( root, price_range.start, info.context.country ) return price.net @staticmethod @gql_optimizer.resolver_hints( prefetch_related=[ "product_type__attributeproduct__productassignments__values", "product_type__attributeproduct__attribute", ] ) def resolve_attributes(root: models.Product, info): return resolve_attribute_list(root, user=info.context.user) @staticmethod @permission_required("product.manage_products") def resolve_purchase_cost(root: models.Product, *_args): purchase_cost, _ = get_product_costs_data(root) return purchase_cost @staticmethod @permission_required("product.manage_products") def resolve_margin(root: models.Product, *_args): _, margin = get_product_costs_data(root) return Margin(margin[0], margin[1]) @staticmethod def resolve_image_by_id(root: models.Product, info, id): pk = get_database_id(info, id, ProductImage) try: return root.images.get(pk=pk) except models.ProductImage.DoesNotExist: raise GraphQLError("Product image not found.") @staticmethod @gql_optimizer.resolver_hints(model_field="images") def resolve_images(root: models.Product, *_args, **_kwargs): return root.images.all() @staticmethod def resolve_variants(root: models.Product, *_args, **_kwargs): return root.variants.all() @staticmethod def resolve_collections(root: models.Product, *_args): return root.collections.all() @classmethod def get_node(cls, info, pk): if info.context: qs = cls._meta.model.objects.visible_to_user(info.context.user) return cls.maybe_optimize(info, qs, pk) return None @staticmethod @permission_required("product.manage_products") def resolve_private_meta(root, _info): return resolve_private_meta(root, _info) @staticmethod def resolve_meta(root, _info): return resolve_meta(root, _info) @staticmethod def resolve_slug(root: models.Product, *_args): return root.get_slug() @staticmethod def __resolve_reference(root, _info, **_kwargs): return graphene.Node.get_node_from_global_id(_info, root.id)
def make_schema(): # Here we dynamically generate an interface similar to this example # https://github.com/graphql-python/graphene-sqlalchemy/blob/master/docs/examples.rst from sqlalchemy.orm.attributes import InstrumentedAttribute import graphene from graphene.relay import Connection, Node from graphene_sqlalchemy import SQLAlchemyObjectType, SQLAlchemyConnectionField # A map of all classes to list of sub-classes class_map = get_class_map(Base.__subclasses__()) # Map of models to SQLAlchemyObjectType sql_map = dict() # Map of models to graphene.Interface ifa_map = dict() # Map of all things available on query, generates Schema queries = dict() IName = type(f'IName', (graphene.Interface, ), { 'name': graphene.String(), 'role': graphene.String() }) for cls, subs in class_map.items(): ifas = [Node] if cls in ifa_map: ifas.extend[ifa_map[cls]] else: ifa = make_graphene_interface(cls) ifas.insert(0, ifa) bases = list(cls.__bases__) while bases: base = bases.pop(0) if base in ifa_map: bases.extend(list(base.__bases__)) ifas.extend(ifa_map[base]) ifas = list(set(ifas)) ifa_map[cls] = ifas[:] print( f'INTERFACES: {cls.__name__} => {", ".join([c.__name__ for c in ifas])}' ) # Name for the Type of X name = cls.__name__.replace('Model', '') # Name for the Type of X and and relationship to Y conn_name = f'{name}Connection' # Name for a query of multiple X query_name = f'{name.lower()}s' SqlType = type( name, (SQLAlchemyObjectType, ), { 'Meta': type('Meta', (), { 'model': cls, 'interfaces': tuple(ifas) }) }) SqlTypeConnection = type(conn_name, (Connection, ), {'Meta': type('Meta', (), {'node': SqlType})}) sql_map[cls] = SqlType queries[f'relay_{query_name}'] = SQLAlchemyConnectionField( SqlTypeConnection) queries[query_name] = graphene.List(SqlType) queries[f'resolve_{query_name}'] = make_resolve_func(SqlType) print(f'{name} => {query_name} => {cls.__name__}') for cls, subs in class_map.items(): if not subs: continue name = cls.__name__.replace('Model', '') sql_types = [sql_map[cls]] + [sql_map[s] for s in subs] # Name for the Type of X and that which is derived from X poly_name = f'Any{name}s' # Name for the query of multiple X and that which is derived from X poly_query_name = f'resolve_{poly_name}' SqlUnionType = type( poly_name, (graphene.Union, ), { 'Meta': type('Meta', (), { 'types': tuple(sql_types), 'interfaces': (IName, ) }), 'resolve_type': make_resolve_type_func(sql_map) }) queries[poly_name] = graphene.List(SqlUnionType) queries[poly_query_name] = make_resolve_func(SqlUnionType) print( f'{poly_name} => {poly_query_name} => {",".join([c.__name__ for c in subs])}' ) # Always present queries['node'] = Node.Field() # Create actual query class Query = type('Query', (graphene.ObjectType, ), queries) schema = graphene.Schema(query=Query) return schema
class PageTypeUpdateInput(PageTypeCreateInput): remove_attributes = graphene.List( graphene.NonNull(graphene.ID), description="List of attribute IDs to be assigned to the page type.", )
def get_input_fields_for_model(model, only_fields, exclude_fields, optional_fields=(), required_fields=(), many_to_many_extras=None, foreign_key_extras=None, many_to_one_extras=None, parent_type_name="", field_types=None) -> OrderedDict: registry = get_global_registry() meta_registry = get_type_meta_registry() model_fields = get_model_fields(model) many_to_many_extras = many_to_many_extras or {} foreign_key_extras = foreign_key_extras or {} many_to_one_extras = many_to_one_extras or {} field_types = field_types or {} fields = OrderedDict() fields_lookup = {} for name, field in model_fields: # We ignore the primary key if getattr(field, "primary_key", False): continue # If the field has an override, use that if name in field_types: fields[name] = field_types[name] continue # Save for later fields_lookup[name] = field is_not_in_only = only_fields and name not in only_fields # is_already_created = name in options.fields is_excluded = name in exclude_fields # or is_already_created # https://docs.djangoproject.com/en/1.10/ref/models/fields/#django.db.models.ForeignKey.related_query_name is_no_backref = str(name).endswith("+") if is_not_in_only or is_excluded or is_no_backref: # We skip this field if we specify only_fields and is not # in there. Or when we exclude this field in exclude_fields. # Or when there is no back reference. continue required = None if name in optional_fields: required = False elif name in required_fields: required = True converted = convert_django_field_with_choices( field, registry, required, many_to_many_extras.get(name, {}).get('exact'), foreign_key_extras.get(name, {})) fields[name] = converted # Create extra many_to_many_fields for name, extras in many_to_many_extras.items(): field = fields_lookup.get(name) if field is None: raise GraphQLError( f"Error adding extras for {name} in model f{model}. Field {name} does not exist." ) for extra_name, data in extras.items(): # This is handled above if extra_name == "exact": continue if isinstance(data, bool): data = {} _type_name = data.get('type') _field = convert_many_to_many_field(field, registry, False, data, None) # Default to the same as the "exact" version if not _field: _field = fields[name] # operation = data.get('operation') or get_likely_operation_from_name(extra_name) fields[name + "_" + extra_name] = _field for name, extras in many_to_one_extras.items(): field = fields_lookup.get(name) if field is None: raise GraphQLError( f"Error adding extras for {name} in model f{model}. Field {name} does not exist." ) for extra_name, data in extras.items(): argument_name = data.get('name', name + "_" + extra_name) # Override default if extra_name == "exact": argument_name = name if isinstance(data, bool): data = {"type": 'ID'} _type = data.get('type') if not _type or _type == "auto": # Create new type. _type_name = data.get( 'type_name', f"{parent_type_name}Create{model.__name__}{name.capitalize()}" ) converted_fields = get_input_fields_for_model( field.related_model, data.get('only_fields', ()), data.get( 'exclude_fields', (field.field.name, ) ), # Exclude the field referring back to the foreign key data.get('optional_fields', ()), data.get('required_fields', ()), data.get('many_to_many_extras'), data.get('foreign_key_extras'), data.get('many_to_one_extras'), parent_type_name=_type_name, field_types=data.get('field_types')) InputType = type(_type_name, (InputObjectType, ), converted_fields) meta_registry.register( _type_name, { 'auto_context_fields': data.get( 'auto_context_fields', {}), 'optional_fields': data.get('optional_fields', ()), 'required_fields': data.get('required_fields', ()), 'many_to_many_extras': data.get( 'many_to_many_extras', {}), 'many_to_one_extras': data.get('many_to_one_extras', {}), 'foreign_key_extras': data.get('auto_context_fields', {}), 'field_types': data.get('field_types', {}), }) _field = graphene.List(type(_type_name, (InputObjectType, ), converted_fields), required=False) else: _field = convert_many_to_many_field(field, registry, False, data, None) fields[argument_name] = _field return fields
class GetNotificationsResponse(graphene.ObjectType, config_types.ResponseFields): notifications = graphene.List(NotificationType)
class Query(object): all_comments_for_task = graphene.List(CommentType, taskId=graphene.String()) def resolve_all_comments_for_task(self, info, taskId , **kwargs): taskInstance = Task.objects.get(id = taskId) return Comment.objects.filter(task = taskInstance)
class StaffInput(UserInput): permissions = graphene.List( PermissionEnum, description='List of permission code names to assign to this user.')
class PostResults(graphene.ObjectType): posts = graphene.List(of_type=PostField)