Change "invalid_coupon" error into a result

This commit is contained in:
Ramon Wenger 2023-05-24 00:26:29 +02:00
parent 5e78d08c48
commit d1664592ce
5 changed files with 131 additions and 69 deletions

View File

@ -7,3 +7,8 @@ class FailureNode(graphene.Interface):
class SuccessNode(graphene.Interface):
message = graphene.String()
class Success(graphene.ObjectType):
class Meta:
interfaces = (SuccessNode,)

View File

@ -1,7 +1,7 @@
import graphene
from graphene import relay
from api.types import FailureNode, SuccessNode
from api.types import FailureNode, Success
from api.utils import get_object
from books.models import Module, ContentBlock, Chapter
from books.models.snapshot import Snapshot
@ -14,23 +14,24 @@ class NotOwner(graphene.ObjectType):
interfaces = (FailureNode,)
class Success(graphene.ObjectType):
class Meta:
interfaces = (SuccessNode,)
NotOwnerFailure = NotOwner(reason="Not the owner")
DeleteSnapshotSuccess = Success(message='Snapshot deleted successfully')
DeleteSnapshotSuccess = Success(message="Snapshot deleted successfully")
class UpdateSnapshotResult(graphene.Union):
class Meta:
types = (SnapshotNode, NotOwner,)
types = (
SnapshotNode,
NotOwner,
)
class DeleteSnapshotResult(graphene.Union):
class Meta:
types = (Success, NotOwner,)
types = (
Success,
NotOwner,
)
class CreateSnapshot(relay.ClientIDMutation):
@ -43,12 +44,12 @@ class CreateSnapshot(relay.ClientIDMutation):
@classmethod
def mutate_and_get_payload(cls, root, info, **args):
module_slug = args.get('module')
module_slug = args.get("module")
module = Module.objects.get(slug=module_slug)
user = info.context.user
# todo: check user
# raise NotImplementedError('Permissions')
selected_class_id = args.get('selected_class')
selected_class_id = args.get("selected_class")
selected_class = get_object(SchoolClass, selected_class_id)
snapshot = Snapshot.objects.create_snapshot(module, selected_class, user)
return cls(snapshot=snapshot, success=True)
@ -63,8 +64,8 @@ class UpdateSnapshot(relay.ClientIDMutation):
@classmethod
def mutate_and_get_payload(cls, root, info, **args):
id = args.get('id')
title = args.get('title')
id = args.get("id")
title = args.get("title")
user = info.context.user
snapshot = get_object(Snapshot, id)
@ -84,7 +85,7 @@ class DeleteSnapshot(relay.ClientIDMutation):
@classmethod
def mutate_and_get_payload(cls, root, info, **args):
id = args.get('id')
id = args.get("id")
user = info.context.user
snapshot = get_object(Snapshot, id)
@ -105,14 +106,17 @@ class ApplySnapshot(relay.ClientIDMutation):
@classmethod
def mutate_and_get_payload(cls, root, info, **args):
snapshot_id = args.get('snapshot')
snapshot_id = args.get("snapshot")
snapshot = get_object(Snapshot, snapshot_id)
user = info.context.user
selected_class_id = args.get('selected_class')
selected_class_id = args.get("selected_class")
selected_class = get_object(SchoolClass, selected_class_id)
# permission check
if not selected_class.users.filter(username=user.username).exists() or not user.is_teacher():
raise PermissionError('Not allowed')
if (
not selected_class.users.filter(username=user.username).exists()
or not user.is_teacher()
):
raise PermissionError("Not allowed")
# reset everything
snapshot.reset(user, selected_class)
@ -131,12 +135,12 @@ class ShareSnapshot(relay.ClientIDMutation):
@classmethod
def mutate_and_get_payload(cls, root, info, **args):
snapshot_id = args.get('snapshot')
shared = args.get('shared')
snapshot_id = args.get("snapshot")
shared = args.get("shared")
user = info.context.user
snapshot = get_object(Snapshot, snapshot_id)
if snapshot.creator != user:
raise PermissionError('Not permitted')
raise PermissionError("Not permitted")
snapshot.shared = shared
snapshot.save()
return cls(success=True, snapshot=snapshot)

View File

@ -5,6 +5,7 @@ from graphene import relay
from oauth.hep_client import HepClient, HepClientException
from oauth.models import OAuth2Token
from oauth.types import InvalidCouponFailure, RedeemCouponResult, RedeemCouponSuccess
from oauth.user_signup_login_handler import create_role_for_user
from users.models import License
@ -13,39 +14,46 @@ class Coupon(relay.ClientIDMutation):
class Input:
coupon_code = graphene.String()
success = graphene.Boolean()
result = RedeemCouponResult()
@classmethod
def mutate_and_get_payload(cls, root, info, **kwargs):
coupon_code = kwargs.get('coupon_code').strip()
coupon_code = kwargs.get("coupon_code").strip()
hep_client = HepClient()
try:
hep_id = info.context.user.hep_id
except AttributeError:
raise Exception('not_authenticated')
raise Exception("not_authenticated")
try:
# todo: differenciate between invalid_coupon and other errors like "delivery_address_not_set"
response = hep_client.redeem_coupon(coupon_code, hep_id, request=info.context)
response = hep_client.redeem_coupon(
coupon_code, hep_id, request=info.context
)
except HepClientException:
raise Exception('unknown_error')
raise Exception("unknown_error")
if not response:
raise Exception('invalid_coupon')
return cls(result=InvalidCouponFailure)
product = hep_client.entry_to_product(response['data'], now(), 'coupon')
product = hep_client.entry_to_product(response["data"], now(), "coupon")
if not product:
raise Exception('non_myskillbox_product')
raise Exception("non_myskillbox_product")
license = License.objects.create_license_for_role(info.context.user, product['activated'], product['raw'],
product['license']['edition'],
product['order_id'], product['isbn'])
license = License.objects.create_license_for_role(
info.context.user,
product["activated"],
product["raw"],
product["license"]["edition"],
product["order_id"],
product["isbn"],
)
create_role_for_user(info.context.user, license.for_role.key)
return cls(success=True)
return cls(result=RedeemCouponSuccess)
class CouponMutations:
@ -77,4 +85,3 @@ class Logout(graphene.Mutation):
class OauthMutations(object):
logout = Logout.Field()
coupon = Coupon.Field()

View File

@ -26,7 +26,7 @@ REDEEM_MYSKILLBOX_SUCCESS_RESPONSE = {
"price": 100,
"price_total": 100,
"amount": 1,
"authors": []
"authors": [],
}
}
@ -45,29 +45,27 @@ REDEEM_OTHER_LICENSE_RESPONSE = {
"price": 100,
"price_total": 100,
"amount": 1,
"authors": []
"authors": [],
}
}
INVALID_LICENSE = {
"message": "The given data was invalid.",
"errors": {
"code": [
"The coupons was already redeemed."
]
}
"errors": {"code": ["The coupons was already redeemed."]},
}
class CouponTests(TestCase):
def setUp(self):
self.user = UserFactory(username='aschi@iterativ.ch', email='aschi@iterativ.ch', hep_id=3)
self.user = UserFactory(
username="aschi@iterativ.ch", email="aschi@iterativ.ch", hep_id=3
)
Role.objects.create_default_roles()
self.teacher_role = Role.objects.get_default_teacher_role()
Oauth2TokenFactory(user=self.user)
# adding session
request = RequestFactory().post('/')
request = RequestFactory().post("/")
middleware = SessionMiddleware()
middleware.process_request(request)
request.user = self.user
@ -75,23 +73,33 @@ class CouponTests(TestCase):
self.client = Client(schema=schema, context_value=request)
def make_coupon_mutation(self, coupon_code, client):
mutation = '''
mutation = """
mutation Coupon($input: CouponInput!){
coupon(input: $input) {
success
result {
__typename
...on InvalidCoupon {
reason
}
...on Success {
message
}
}
}
}
'''
"""
return client.execute(mutation, variables={
'input': {
'couponCode': coupon_code
}
})
return client.execute(
mutation, variables={"input": {"couponCode": coupon_code}}
)
@patch.object(BaseApp, 'post', return_value=MockResponse(200, data=REDEEM_MYSKILLBOX_SUCCESS_RESPONSE))
@patch.object(
BaseApp,
"post",
return_value=MockResponse(200, data=REDEEM_MYSKILLBOX_SUCCESS_RESPONSE),
)
def test_user_has_valid_skillbox_coupon(self, response_mock):
result = self.make_coupon_mutation('COUPON--1234', self.client)
result = self.make_coupon_mutation("COUPON--1234", self.client)
user_role = self.user.user_roles.get(user=self.user)
self.assertEqual(user_role.role.key, Role.objects.TEACHER_KEY)
@ -101,48 +109,66 @@ class CouponTests(TestCase):
school_class = SchoolClass.objects.get(users__in=[self.user])
self.assertIsNotNone(school_class)
self.assertTrue(result.get('data').get('coupon').get('success'))
self.assertTrue(result.get("data").get("coupon").get("success"))
self.assertTrue(self.user.is_authenticated)
@patch.object(BaseApp, 'post', return_value=MockResponse(200, data=REDEEM_OTHER_LICENSE_RESPONSE))
@patch.object(
BaseApp,
"post",
return_value=MockResponse(200, data=REDEEM_OTHER_LICENSE_RESPONSE),
)
def test_user_has_valid_non_skillbox_coupon(self, response_mock):
result = self.make_coupon_mutation('COUPON--1234', self.client)
result = self.make_coupon_mutation("COUPON--1234", self.client)
try:
self.user.user_roles.get(user=self.user)
self.fail("CouponTests.test_user_has_valid_non_skillbox_coupon: Should not have created user role")
self.fail(
"CouponTests.test_user_has_valid_non_skillbox_coupon: Should not have created user role"
)
except:
pass
try:
License.objects.get(licensee=self.user)
self.fail("CouponTests.test_user_has_valid_non_skillbox_coupon: License should not exist")
self.fail(
"CouponTests.test_user_has_valid_non_skillbox_coupon: License should not exist"
)
except License.DoesNotExist:
pass
self.assertEqual(result.get('errors')[0].get('message'), 'non_myskillbox_product')
self.assertEqual(
result.get("errors")[0].get("message"), "non_myskillbox_product"
)
self.assertTrue(self.user.is_authenticated)
@patch.object(BaseApp, 'post', return_value=MockResponse(404, data=INVALID_LICENSE))
@patch.object(BaseApp, "post", return_value=MockResponse(404, data=INVALID_LICENSE))
def test_user_has_invalid_coupon(self, response_mock):
result = self.make_coupon_mutation('COUPON--1234', self.client)
result = self.make_coupon_mutation("COUPON--1234", self.client)
self.assertEqual(result.get('errors')[0].get('message'), 'invalid_coupon')
self.assertEqual(
result.get("data").get("coupon").get("result").get("__typename"),
"InvalidCoupon",
)
@patch.object(BaseApp, 'post', return_value=MockResponse(422, data=INVALID_LICENSE))
@patch.object(BaseApp, "post", return_value=MockResponse(422, data=INVALID_LICENSE))
def test_user_has_already_used_coupon(self, response_mock):
result = self.make_coupon_mutation('COUPON--1234', self.client)
result = self.make_coupon_mutation("COUPON--1234", self.client)
self.assertEqual(result.get('errors')[0].get('message'), 'invalid_coupon')
self.assertEqual(
result.get("data").get("coupon").get("result").get("__typename"),
"InvalidCoupon",
)
@patch.object(BaseApp, 'put', return_value=MockResponse(200, data=['201', 'Invalid Coupon']))
@patch.object(
BaseApp, "put", return_value=MockResponse(200, data=["201", "Invalid Coupon"])
)
def test_unauthenticated_user_cannot_redeem(self, response_mock):
request = RequestFactory().post('/')
request = RequestFactory().post("/")
middleware = SessionMiddleware()
middleware.process_request(request)
request.session.save()
client = Client(schema=schema, context_value=request)
result = self.make_coupon_mutation('COUPON--1234', client)
result = self.make_coupon_mutation("COUPON--1234", client)
self.assertEqual(result.get('errors')[0].get('message'), 'not_authenticated')
self.assertEqual(result.get("errors")[0].get("message"), "not_authenticated")

20
server/oauth/types.py Normal file
View File

@ -0,0 +1,20 @@
import graphene
from api.types import FailureNode, Success
class InvalidCoupon(graphene.ObjectType):
class Meta:
interfaces = (FailureNode,)
RedeemCouponSuccess = Success(message="Coupon successfully redeemed.")
InvalidCouponFailure = InvalidCoupon(reason="Invalid coupon provided.")
class RedeemCouponResult(graphene.Union):
class Meta:
types = (
Success,
InvalidCoupon,
)