Beispiel #1
0
 def process_request(self, request):
     """
     Stores whether or not FORCE_DJANGO_CACHE_MISS_KEY was supplied in the
     request. Also, clears the request cache.
     """
     RequestCache.clear()
     TieredCache._get_and_set_force_cache_miss(request)  # pylint: disable=protected-access
Beispiel #2
0
 def test_delete_missing_key(self):
     try:
         RequestCache.delete(TEST_KEY)
     except KeyError:
         self.fail(
             'Deleting a missing key from the request cache should not cause an error.'
         )
Beispiel #3
0
    def test_process_request(self):
        self.middleware.process_request(self.request)

        self.assertTrue(RequestCache.get_cached_response(TEST_KEY).is_miss)
        self.assertFalse(
            RequestCache.get_cached_response(
                SHOULD_FORCE_CACHE_MISS_KEY).value)
Beispiel #4
0
 def test_delete(self, mock_cache_delete):
     TieredCache.set_all_tiers(TEST_KEY, EXPECTED_VALUE)
     TieredCache.set_all_tiers(TEST_KEY_2, EXPECTED_VALUE)
     TieredCache.delete_all_tiers(TEST_KEY)
     self.assertTrue(RequestCache.get_cached_response(TEST_KEY).is_miss)
     self.assertEqual(
         RequestCache.get_cached_response(TEST_KEY_2).value, EXPECTED_VALUE)
     mock_cache_delete.assert_called_with(TEST_KEY)
Beispiel #5
0
    def test_process_request_force_django_cache_miss(self):
        request = RequestFactory().get(
            '/?{}=tRuE'.format(FORCE_CACHE_MISS_PARAM))

        self.middleware.process_request(request)

        self.assertTrue(RequestCache.get_cached_response(TEST_KEY).is_miss)
        self.assertTrue(
            RequestCache.get_cached_response(
                SHOULD_FORCE_CACHE_MISS_KEY).value)
Beispiel #6
0
    def test_get_cached_response_force_django_cache_miss(self, mock_cache_get):
        RequestCache.set(SHOULD_FORCE_CACHE_MISS_KEY, True)
        mock_cache_get.return_value = EXPECTED_VALUE
        cached_response = TieredCache.get_cached_response(TEST_KEY)
        self.assertTrue(cached_response.is_miss)

        cached_response = RequestCache.get_cached_response(TEST_KEY)
        self.assertTrue(
            cached_response.is_miss,
            'Forced Django cache miss should not cache value in request cache.'
        )
Beispiel #7
0
    def test_flush_with_product_is_not_tracked_for_temporary_basket_calculation(
            self):
        """
        Verify the method does NOT fire 'Product Removed' Segment for temporary basket calculation
        """
        basket = self._create_basket_with_product()
        RequestCache.set(TEMPORARY_BASKET_CACHE_KEY, True)

        with mock.patch.object(Client, 'track') as mock_track:
            basket.flush()
            mock_track.assert_not_called()
Beispiel #8
0
 def test_set_all_tiers(self, mock_cache_set):
     mock_cache_set.return_value = EXPECTED_VALUE
     TieredCache.set_all_tiers(TEST_KEY, EXPECTED_VALUE,
                               TEST_DJANGO_TIMEOUT_CACHE)
     mock_cache_set.assert_called_with(TEST_KEY, EXPECTED_VALUE,
                                       TEST_DJANGO_TIMEOUT_CACHE)
     self.assertEqual(
         RequestCache.get_cached_response(TEST_KEY).value, EXPECTED_VALUE)
Beispiel #9
0
 def test_add_product_not_tracked_for_temporary_basket_calculation(self):
     """
     Verify the method does NOT fire Product Added analytic event when a product is added to the basket
     """
     course = CourseFactory()
     basket = create_basket(empty=True)
     seat = course.create_or_update_seat('verified', True, 100,
                                         self.partner)
     RequestCache.set(TEMPORARY_BASKET_CACHE_KEY, True)
     with mock.patch(
             'ecommerce.extensions.basket.models.track_segment_event'
     ) as mock_track:
         basket.add_product(seat)
         properties = translate_basket_line_for_segment(
             basket.lines.first())
         properties['cart_id'] = basket.id
         mock_track.assert_not_called()
Beispiel #10
0
    def test_delete(self):
        RequestCache.set(TEST_KEY, EXPECTED_VALUE)
        RequestCache.set(TEST_KEY_2, EXPECTED_VALUE)
        RequestCache.delete(TEST_KEY)

        cached_response = RequestCache.get_cached_response(TEST_KEY)
        self.assertTrue(cached_response.is_miss)

        cached_response = RequestCache.get_cached_response(TEST_KEY_2)
        self.assertTrue(cached_response.is_hit)
        self.assertEqual(cached_response.value, EXPECTED_VALUE)
Beispiel #11
0
    def test_get_cached_response_django_cache_hit(self, mock_cache_get):
        mock_cache_get.return_value = EXPECTED_VALUE
        cached_response = TieredCache.get_cached_response(TEST_KEY)
        self.assertTrue(cached_response.is_hit)
        self.assertEqual(cached_response.value, EXPECTED_VALUE)

        cached_response = RequestCache.get_cached_response(TEST_KEY)
        self.assertTrue(
            cached_response.is_hit,
            'Django cache hit should cache value in request cache.')
Beispiel #12
0
    def flush(self):
        """Remove all products in basket and fire Segment 'Product Removed' Analytic event for each"""
        cached_response = RequestCache.get_cached_response(TEMPORARY_BASKET_CACHE_KEY)
        if cached_response.is_hit:
            # Do not track anything. This is a temporary basket calculation.
            return
        for line in self.all_lines():
            # Do not fire events for free items. The volume we see for edX.org leads to a dramatic increase in CPU
            # usage. Given that orders for free items are ignored, there is no need for these events.
            if line.stockrecord.price_excl_tax > 0:
                properties = translate_basket_line_for_segment(line)
                track_segment_event(self.site, self.owner, 'Product Removed', properties)

        # Call flush after we fetch all_lines() which is cleared during flush()
        super(Basket, self).flush()  # pylint: disable=bad-super-call
Beispiel #13
0
    def add_product(self, product, quantity=1, options=None):
        """
        Add the indicated product to basket.

        Performs AbstractBasket add_product method and fires Google Analytics 'Product Added' event.
        """
        line, created = super(Basket, self).add_product(product, quantity, options)  # pylint: disable=bad-super-call
        cached_response = RequestCache.get_cached_response(TEMPORARY_BASKET_CACHE_KEY)
        if cached_response.is_hit:
            # Do not track anything. This is a temporary basket calculation.
            return line, created

        # Do not fire events for free items. The volume we see for edX.org leads to a dramatic increase in CPU
        # usage. Given that orders for free items are ignored, there is no need for these events.
        if line.stockrecord.price_excl_tax > 0:
            properties = translate_basket_line_for_segment(line)
            properties['cart_id'] = self.id
            track_segment_event(self.site, self.owner, 'Product Added', properties)
        return line, created
Beispiel #14
0
 def test_get_cached_response_request_cache_hit(self):
     RequestCache.set(TEST_KEY, EXPECTED_VALUE)
     cached_response = TieredCache.get_cached_response(TEST_KEY)
     self.assertTrue(cached_response.is_hit)
     self.assertEqual(cached_response.value, EXPECTED_VALUE)
Beispiel #15
0
 def test_clear(self):
     RequestCache.set(TEST_KEY, EXPECTED_VALUE)
     RequestCache.clear()
     cached_response = RequestCache.get_cached_response(TEST_KEY)
     self.assertTrue(cached_response.is_miss)
Beispiel #16
0
 def test_get_cached_response_miss(self):
     cached_response = RequestCache.get_cached_response(TEST_KEY)
     self.assertTrue(cached_response.is_miss)
Beispiel #17
0
 def test_get_cached_response_hit_with_cached_none(self):
     RequestCache.set(TEST_KEY, None)
     cached_response = RequestCache.get_cached_response(TEST_KEY)
     self.assertFalse(cached_response.is_miss)
     self.assertEqual(cached_response.value, None)
Beispiel #18
0
 def test_get_cached_response_hit(self):
     RequestCache.set(TEST_KEY, EXPECTED_VALUE)
     cached_response = RequestCache.get_cached_response(TEST_KEY)
     self.assertFalse(cached_response.is_miss)
     self.assertEqual(cached_response.value, EXPECTED_VALUE)
Beispiel #19
0
 def setUp(self):
     RequestCache.clear()
Beispiel #20
0
 def process_response(self, request, response):  # pylint: disable=unused-argument
     """
      Clear the request cache after processing a response.
      """
     RequestCache.clear()
     return response
Beispiel #21
0
 def _dirty_request_cache():
     """ Dirties the request cache to ensure it is cleared later. """
     RequestCache.set(TEST_KEY, EXPECTED_VALUE)
Beispiel #22
0
    def test_process_exception(self):
        response = self.middleware.process_exception(self.request,
                                                     EXPECTED_VALUE)

        self.assertEqual(response, None)
        self.assertTrue(RequestCache.get_cached_response(TEST_KEY).is_miss)
Beispiel #23
0
 def process_exception(self, request, exception):  # pylint: disable=unused-argument
     """
     Clear the request cache after a failed request.
     """
     RequestCache.clear()
     return None
Beispiel #24
0
    def get(self, request):  # pylint: disable=too-many-statements
        """ Calculate basket totals given a list of sku's

        Create a temporary basket add the sku's and apply an optional voucher code.
        Then calculate the total price less discounts. If a voucher code is not
        provided apply a voucher in the Enterprise entitlements available
        to the user.

        Query Params:
            sku (string): A list of sku(s) to calculate
            code (string): Optional voucher code to apply to the basket.
            username (string): Optional username of a user for which to calculate the basket.

        Returns:
            JSON: {
                    'total_incl_tax_excl_discounts': basket.total_incl_tax_excl_discounts,
                    'total_incl_tax': basket.total_incl_tax,
                    'currency': basket.currency
                }
        """
        RequestCache.set(TEMPORARY_BASKET_CACHE_KEY,
                         True)  # TODO: LEARNER 5463

        partner = get_partner_for_site(request)
        skus = request.GET.getlist('sku')
        if not skus:
            return HttpResponseBadRequest(_('No SKUs provided.'))
        skus.sort()

        code = request.GET.get('code', None)
        try:
            voucher = Voucher.objects.get(code=code) if code else None
        except Voucher.DoesNotExist:
            voucher = None

        products = Product.objects.filter(stockrecords__partner=partner,
                                          stockrecords__partner_sku__in=skus)
        if not products:
            return HttpResponseBadRequest(
                _('Products with SKU(s) [{skus}] do not exist.').format(
                    skus=', '.join(skus)))

        # If there is only one product apply an Enterprise entitlement voucher
        if not voucher and len(products) == 1:
            voucher = get_entitlement_voucher(request, products[0])

        basket_owner = request.user

        requested_username = request.GET.get('username', default='')
        is_anonymous = request.GET.get('is_anonymous',
                                       'false').lower() == 'true'

        use_default_basket = is_anonymous

        # validate query parameters
        if requested_username and is_anonymous:
            return HttpResponseBadRequest(
                _('Provide username or is_anonymous query param, but not both')
            )
        elif not requested_username and not is_anonymous:
            logger.warning(
                "Request to Basket Calculate must supply either username or is_anonymous query"
                " param. Requesting user=%s. Future versions of this API will treat this "
                "WARNING as an ERROR and raise an exception.",
                basket_owner.username)
            requested_username = request.user.username

        # If a username is passed in, validate that the user has staff access or is the same user.
        if requested_username:
            if basket_owner.username.lower() == requested_username.lower():
                pass
            elif basket_owner.is_staff:
                try:
                    basket_owner = User.objects.get(
                        username=requested_username)
                except User.DoesNotExist:
                    # This case represents a user who is logged in to marketing, but
                    # doesn't yet have an account in ecommerce. These users have
                    # never purchased before.
                    use_default_basket = True
            else:
                return HttpResponseForbidden('Unauthorized user credentials')

        if basket_owner.username == self.MARKETING_USER and not use_default_basket:
            # For legacy requests that predate is_anonymous parameter, we will calculate
            # an anonymous basket if the calculated user is the marketing user.
            # TODO: LEARNER-5057: Remove this special case for the marketing user
            # once logs show no more requests with no parameters (see above).
            use_default_basket = True

        if use_default_basket:
            basket_owner = None

        cache_key = None
        if use_default_basket:
            # For an anonymous user we can directly get the cached price, because
            # there can't be any enrollments or entitlements.
            cache_key = get_cache_key(site_comain=request.site,
                                      resource_name='calculate',
                                      skus=skus)
            cached_response = TieredCache.get_cached_response(cache_key)
            if cached_response.is_hit:
                return Response(cached_response.value)

        if waffle.flag_is_active(
                request,
                "disable_calculate_temporary_basket_atomic_transaction"):
            response = self._calculate_temporary_basket(
                basket_owner, request, products, voucher, skus, code)
        else:
            response = self._calculate_temporary_basket_atomic(
                basket_owner, request, products, voucher, skus, code)

        if response and use_default_basket:
            TieredCache.set_all_tiers(
                cache_key, response,
                settings.ANONYMOUS_BASKET_CALCULATE_CACHE_TIMEOUT)

        return Response(response)
Beispiel #25
0
 def test_clear_all_tiers(self, mock_cache_clear):
     TieredCache.set_all_tiers(TEST_KEY, EXPECTED_VALUE)
     TieredCache.clear_all_tiers()
     self.assertTrue(RequestCache.get_cached_response(TEST_KEY).is_miss)
     mock_cache_clear.assert_called_once_with()