Skip to content

Commit

Permalink
Merge pull request #203 from openedx/hu/ent-7114
Browse files Browse the repository at this point in the history
feat: idempotent policies
  • Loading branch information
brobro10000 authored Jun 26, 2023
2 parents c3ff6f7 + d01be48 commit dda40f5
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 1 deletion.
18 changes: 18 additions & 0 deletions enterprise_access/apps/api/serializers/subsidy_access_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,27 @@ class Meta:
},
}

@property
def calling_view(self):
"""
Return the view that called this serializer.
"""
return self.context['view']

def create(self, validated_data):
policy_type = validated_data.get('policy_type')
policy_model = apps.get_model(app_label='subsidy_access_policy', model_name=policy_type)
filtered_policy = policy_model.objects.filter(
enterprise_customer_uuid=validated_data['enterprise_customer_uuid'],
subsidy_uuid=validated_data['subsidy_uuid'],
catalog_uuid=validated_data['catalog_uuid'],
access_method=validated_data['access_method'],
active=True,
).first()
if filtered_policy:
self.calling_view.set_policy_created(False)
return filtered_policy
self.calling_view.set_policy_created(True)
policy = policy_model.objects.create(**validated_data)
return policy

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,64 @@ def test_create_view(self, policy_type, extra_fields, expected_response_code, ex
for expected_error_keyword in expected_error_keywords:
assert expected_error_keyword in response.content.decode("utf-8")

@ddt.data(
{
'policy_type': PolicyTypes.PER_LEARNER_SPEND_CREDIT,
'extra_fields': {
'per_learner_spend_limit': 30000,
},
'expected_response_code': status.HTTP_201_CREATED,
}
)
@ddt.unpack
def test_idempotent_create_view(self, policy_type, extra_fields, expected_response_code):
"""
Test the (deprecated) policy create view's idempotency.
"""
# Set the JWT-based auth that we'll use for every request
self.set_jwt_cookie([{'system_wide_role': SYSTEM_ENTERPRISE_ADMIN_ROLE, 'context': str(TEST_ENTERPRISE_UUID)}])

# Test the retrieve endpoint
create_url = SUBSIDY_ACCESS_POLICY_ADMIN_LIST_ENDPOINT
enterprise_customer_uuid = str(TEST_ENTERPRISE_UUID)
catalog_uuid = str(uuid4())
subsidy_uuid = str(uuid4())
payload = {
'policy_type': policy_type,
'description': 'test description',
'active': True,
'enterprise_customer_uuid': enterprise_customer_uuid,
'catalog_uuid': catalog_uuid,
'subsidy_uuid': subsidy_uuid,
'access_method': AccessMethods.DIRECT,
'spend_limit': None,
}
payload.update(extra_fields)
response = self.client.post(create_url, payload)
assert response.status_code == expected_response_code

if expected_response_code == status.HTTP_201_CREATED:
response_json = response.json()
del response_json['uuid']
expected_response = payload.copy()
expected_response.setdefault("per_learner_enrollment_limit")
expected_response.setdefault("per_learner_spend_limit")
assert response_json == expected_response

# Test idempotency
response = self.client.post(create_url, payload)
duplicate_status_code = status.HTTP_200_OK

assert response.status_code == duplicate_status_code

if response.status_code == status.HTTP_200_OK:
response_json = response.json()
del response_json['uuid']
expected_response = payload.copy()
expected_response.setdefault("per_learner_enrollment_limit")
expected_response.setdefault("per_learner_spend_limit")
assert response_json == expected_response


@ddt.ddt
class TestPolicyRedemptionAuthNAndPermissionChecks(APITestWithMocks):
Expand Down
12 changes: 11 additions & 1 deletion enterprise_access/apps/api/v1/views/subsidy_access_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@ class SubsidyAccessPolicyCRUDViewset(PermissionRequiredMixin, viewsets.ModelView
# impossible for learners to create policies.
permission_required = REQUESTS_ADMIN_LEARNER_ACCESS_PERMISSION

def __init__(self, *args, **kwargs):
self.extra_context = {}
super().__init__(*args, **kwargs)

def set_policy_created(self, created):
self.extra_context['created'] = created

@property
def requested_enterprise_customer_uuid(self):
"""
Expand Down Expand Up @@ -183,11 +190,14 @@ def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=policy_data)
serializer.is_valid(raise_exception=True)
serializer.save()
policy_created = self.extra_context.get('created')
send_access_policy_event_to_event_bus(
ACCESS_POLICY_CREATED.event_type,
serializer.data
)
return Response(serializer.data, status=status.HTTP_201_CREATED)
if policy_created:
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.data, status=status.HTTP_200_OK)

@extend_schema(
tags=[SUBSIDY_ACCESS_POLICY_CRUD_API_TAG],
Expand Down

0 comments on commit dda40f5

Please sign in to comment.