Skip to content

Commit

Permalink
Add config class to get settings
Browse files Browse the repository at this point in the history
We will need to be able to alter settings based on e.g. the request, so
as a first step, I'm introducing a layer between Django settings and the
Adyen code.

We introduce the concept of a config class, that can be set from the
Django settings. It defaults to a supplied config class that just
fetches the needed values from the Django settings as before. But now we
have the possibility of switiching out the config class to fetch those
values from elsewhere.
  • Loading branch information
maiksprenger committed Jul 8, 2015
1 parent e0ef363 commit 9df0ce6
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 17 deletions.
35 changes: 35 additions & 0 deletions adyen/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from django.conf import settings
from django.utils.module_loading import import_string


def get_config():
"""
Returns an instance of the configured config class.
"""

try:
config_class_string = settings.ADYEN_CONFIG_CLASS
except AttributeError:
config_class_string = 'adyen.settings_config.FromSettingsConfig'
return import_string(config_class_string)()


class AbstractAdyenConfig:
"""
The base implementation for a config class.
"""

def get_identifier(self):
raise NotImplementedError

def get_action_url(self):
raise NotImplementedError

def get_skin_code(self):
raise NotImplementedError

def get_skin_secret(self):
raise NotImplementedError

def get_ip_address_header(self):
raise NotImplementedError
15 changes: 6 additions & 9 deletions adyen/facade.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
import iptools
import logging

from django.conf import settings
from django.http import HttpResponse

from .gateway import Constants, Gateway, PaymentNotification, PaymentRedirection
from .models import AdyenTransaction
from .config import get_config

logger = logging.getLogger('adyen')

Expand All @@ -16,9 +16,9 @@ class Facade():

def __init__(self, **kwargs):

This comment has been minimized.

Copy link
@aaugustin

aaugustin Jul 8, 2015

Is the Facade a singleton throughout the life of the Python process? Or is it instantiated at every request?

In the latter case, what about self.config = get_config(), then looking up everything in self.config? That would avoid instantiating config objects repeatedly.

This comment has been minimized.

Copy link
@maiksprenger

maiksprenger Jul 8, 2015

Author Member

That would definitely work. I'm trying to reduce state, you're trying to reduce number of instantiations. I'm happy to go with either.

init_params = {
Constants.IDENTIFIER: settings.ADYEN_IDENTIFIER,
Constants.SECRET_KEY: settings.ADYEN_SECRET_KEY,
Constants.ACTION_URL: settings.ADYEN_ACTION_URL,
Constants.IDENTIFIER: get_config().get_identifier(),
Constants.SECRET_KEY: get_config().get_skin_secret(),
Constants.ACTION_URL: get_config().get_action_url(),
}
# Initialize the gateway.
self.gateway = Gateway(init_params)
Expand Down Expand Up @@ -54,10 +54,7 @@ def _get_origin_ip_address(cls, request):
Django setting. We fallback on the canonical `REMOTE_ADDR`, used for
regular, unproxied requests.
"""
try:
ip_address_http_header = settings.ADYEN_IP_ADDRESS_HTTP_HEADER
except AttributeError:
ip_address_http_header = 'REMOTE_ADDR'
ip_address_http_header = get_config().get_ip_address_header()

try:
ip_address = request.META[ip_address_http_header]
Expand Down Expand Up @@ -209,7 +206,7 @@ def assess_notification_relevance(self, request):
# - On the other hand we have the `live` POST parameter, which lets
# us know which Adyen platform fired this request.
current_platform = (Constants.LIVE
if Constants.LIVE in settings.ADYEN_ACTION_URL
if Constants.LIVE in get_config().get_action_url()
else Constants.TEST)

origin_platform = (Constants.LIVE
Expand Down
12 changes: 4 additions & 8 deletions adyen/scaffold.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@

import bleach

from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.utils import timezone

from .facade import Facade
from .gateway import Constants, MissingFieldException
from .config import get_config


class Scaffold():
Expand Down Expand Up @@ -36,10 +35,7 @@ def __init__(self, order_data=None):

def get_form_action(self):
""" Return the URL where the payment form should be submitted. """
try:
return settings.ADYEN_ACTION_URL
except AttributeError:
raise ImproperlyConfigured("Please set ADYEN_ACTION_URL")
return get_config().get_action_url()

This comment has been minimized.

Copy link
@aaugustin

aaugustin Jul 8, 2015

If my suggestion above makes sense, this would become self.facade.config.get_action_url().


def get_form_fields(self):
""" Return the payment form fields, rendered into HTML. """
Expand All @@ -64,13 +60,13 @@ def get_form_fields_list(self):
# Build common field specs
try:
field_specs = {
Constants.MERCHANT_ACCOUNT: settings.ADYEN_IDENTIFIER,
Constants.MERCHANT_ACCOUNT: get_config().get_identifier(),
Constants.MERCHANT_REFERENCE: str(self.order_number),
Constants.SHOPPER_REFERENCE: self.client_id,
Constants.SHOPPER_EMAIL: self.client_email,
Constants.CURRENCY_CODE: self.currency_code,
Constants.PAYMENT_AMOUNT: self.amount,
Constants.SKIN_CODE: settings.ADYEN_SKIN_CODE,
Constants.SKIN_CODE: get_config().get_skin_code(),
Constants.SESSION_VALIDITY: session_validity.strftime(session_validity_format),
Constants.SHIP_BEFORE_DATE: ship_before_date.strftime(ship_before_date_format),
Constants.SHOPPER_LOCALE: self.shopper_locale,
Expand Down
43 changes: 43 additions & 0 deletions adyen/settings_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured

from .config import AbstractAdyenConfig


class FromSettingsConfig(AbstractAdyenConfig):
"""
This config class is enabled by default and useful in simple deployments.
One can just set all needed values in the Django settings. It also
exists for backwards-compatibility with previous deployments.
"""

def __init__(self):
"""
We complain as early as possible when Django settings are missing.
"""
required_settings = [
'ADYEN_IDENTIFIER', 'ADYEN_ACTION_URL', 'ADYEN_SKIN_CODE', 'ADYEN_SECRET_KEY']
missing_settings = [
setting for setting in required_settings if not hasattr(settings, setting)]
if missing_settings:
raise ImproperlyConfigured(
"You are using the FromSettingsConfig config class, but haven't set the "
"the following required settings: %s" % missing_settings)

def get_identifier(self):
return settings.ADYEN_IDENTIFIER

def get_action_url(self):
return settings.ADYEN_ACTION_URL

def get_skin_code(self):
return settings.ADYEN_SKIN_CODE

def get_skin_secret(self):
return settings.ADYEN_SECRET_KEY

def get_ip_address_header(self):
try:
return settings.ADYEN_IP_ADDRESS_HTTP_HEADER
except AttributeError:
return 'REMOTE_ADDR'

0 comments on commit 9df0ce6

Please sign in to comment.