Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ omit =
*/python?.?/*
*/site-packages/*
*/unittest2/*
*/tests/*
*_flymake.py
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
language: python
python:
- '2.7'
- '3.4'
- '3.5'

install:
Expand Down
12 changes: 10 additions & 2 deletions membersuite_api_client/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,23 @@ def __str__(self):
concierge_error=concierge_error)

def get_concierge_error(self):
return (self.result["body"][self.result_type]
["Errors"]["ConciergeError"])
try:
return (self.result["body"][self.result_type]
["Errors"]["ConciergeError"])
except KeyError:
return (self.result["Errors"])


class LoginToPortalError(MemberSuiteAPIError):

result_type = "LoginToPortalResult"


class LogoutError(MemberSuiteAPIError):

result_type = "LogoutResult"


class ExecuteMSQLError(MemberSuiteAPIError):

result_type = "ExecuteMSQLResult"
2 changes: 1 addition & 1 deletion membersuite_api_client/memberships/models.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
class Membership(object):

def __init__(self, membership):
""" Create a Membership model from MemberSuite Member object
""" Create a Membership model from MemberSuite Membership object
"""
self.id = membership["ID"]
self.owner = membership["Owner"]
Expand Down
2 changes: 1 addition & 1 deletion membersuite_api_client/memberships/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
http://api.docs.membersuite.com/#References/Objects/Membership.htm

"""
from zeep.exceptions import TransportError

from .models import Membership, MembershipProduct
from ..utils import convert_ms_object
from zeep.exceptions import TransportError


class MembershipService(object):
Expand Down
5 changes: 4 additions & 1 deletion membersuite_api_client/organizations/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ def __init__(self, org):
"""Create an Organization model from MemberSuite Organization object
"""
self.account_num = org["ID"]
self.id = self.account_num
self.membersuite_id = org["LocalID"]
self.local_id = self.membersuite_id
self.org_name = org["Name"]
self.name = self.org_name
self.picklist_name = org["SortName"] or ''

self.address = org["Mailing_Address"]
Expand Down Expand Up @@ -44,4 +47,4 @@ def __init__(self, org_type):
'6faf90e4-01f3-c0f1-4593-0b3c3ca7ff6c': 'Deceased',
'6faf90e4-01f3-c7ad-174c-0b3c52b7f497': 'Defunct',
'6faf90e4-01f3-cd50-ffed-0b3c3ca7f4fd': 'Retired',
}
}
70 changes: 68 additions & 2 deletions membersuite_api_client/security/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

from ..exceptions import ExecuteMSQLError
from ..models import MemberSuiteObject
from ..memberships import services as membership_services
from ..organizations.models import Organization
from ..utils import convert_ms_object


Expand Down Expand Up @@ -36,6 +38,9 @@ def __str__(self):
session_id=self.session_id))

def get_username(self):
"""Return a username suitable for storing in auth.User.username.

"""
return "_membersuite_id_{}".format(self.id)

def get_individual(self, client):
Expand All @@ -55,11 +60,12 @@ def get_individual(self, client):
if msql_result["Success"]:
membersuite_object_data = (msql_result["ResultValue"]
["SingleObject"])
return Individual(membersuite_object_data=membersuite_object_data,
portal_user=self)
else:
raise ExecuteMSQLError(result=result)

return Individual(membersuite_object_data=membersuite_object_data,
portal_user=self)


@python_2_unicode_compatible
class Individual(MemberSuiteObject):
Expand All @@ -76,6 +82,9 @@ def __init__(self, membersuite_object_data, portal_user=None):
self.first_name = self.fields["FirstName"]
self.last_name = self.fields["LastName"]

self.primary_organization__rtg = (
self.fields["PrimaryOrganization__rtg"])

self.portal_user = portal_user

def __str__(self):
Expand All @@ -85,3 +94,60 @@ def __str__(self):
email_address=self.email_address,
first_name=self.first_name,
last_name=self.last_name))

def is_member(self, client):
"""Is this Individual a member?

Assumptions:

- a "primary organization" in MemberSuite is the "current"
Organization for an Individual

- get_memberships_for_org() returns Memberships ordered such
that the first one returned is the "current" one.

"""
if not client.session_id:
client.request_session()

primary_organization = self.get_primary_organization(client=client)

membership_service = membership_services.MembershipService(
client=client)

try:
membership = membership_service.get_memberships_for_org(
account_num=primary_organization.id)[0]
except IndexError:
return False

return membership.receives_member_benefits

def get_primary_organization(self, client):
"""Return the primary Organization for this Individual.

"""
if self.primary_organization__rtg is None:
return None

if not client.session_id:
client.request_session()

query = "SELECT OBJECT() FROM ORGANIZATION WHERE ID = '{}'".format(
self.primary_organization__rtg)

result = client.runSQL(query)

msql_result = result["body"]["ExecuteMSQLResult"]

if msql_result["Success"]:
membersuite_object_data = (msql_result["ResultValue"]
["SingleObject"])
else:
raise ExecuteMSQLError(result=result)

# Could omit this step if Organization inherits from MemberSuiteObject.
organization = convert_ms_object(
membersuite_object_data["Fields"]["KeyValueOfstringanyType"])

return Organization(org=organization)
59 changes: 45 additions & 14 deletions membersuite_api_client/security/services.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
from .models import PortalUser
from ..exceptions import LoginToPortalError
from ..exceptions import LoginToPortalError, LogoutError
from ..utils import get_session_id


def login_to_portal(username, password, client):
def login_to_portal(username, password, client, retries=2):
"""Log `username` into the MemberSuite Portal.

Returns a PortalUser object if successful, raises
LoginToPortalError if not.

Will retry logging in if a GeneralException occurs, up to `retries`.
"""
if not client.session_id:
client.request_session()
Expand All @@ -17,19 +18,49 @@ def login_to_portal(username, password, client):
url=("http://membersuite.com/contracts/IConciergeAPIService/"
"LoginToPortal"))

result = client.client.service.LoginToPortal(
_soapheaders=[concierge_request_header],
portalUserName=username,
portalPassword=password)
attempts = 0
while attempts < retries:
result = client.client.service.LoginToPortal(
_soapheaders=[concierge_request_header],
portalUserName=username,
portalPassword=password)

login_to_portal_result = result["body"]["LoginToPortalResult"]
login_to_portal_result = result["body"]["LoginToPortalResult"]

if login_to_portal_result["Success"]:
portal_user = login_to_portal_result["ResultValue"]["PortalUser"]
if login_to_portal_result["Success"]:
portal_user = login_to_portal_result["ResultValue"]["PortalUser"]

session_id = get_session_id(result=result)
session_id = get_session_id(result=result)

return PortalUser(membersuite_object_data=portal_user,
session_id=session_id)
else:
raise LoginToPortalError(result=result)
return PortalUser(membersuite_object_data=portal_user,
session_id=session_id)
else:
attempts += 1

raise LoginToPortalError(result=result)


def logout(client):
"""Log out the currently logged-in user.

There's a really crappy side-effect here - the session_id
attribute of the `client` passed in will be reset to None if the
logout succeeds, which is going to be almost always, let's hope.

"""
if not client.session_id:
client.request_session()

concierge_request_header = client.construct_concierge_header(
url=("http://membersuite.com/contracts/IConciergeAPIService/"
"Logout"))

logout_result = client.client.service.Logout(
_soapheaders=[concierge_request_header])

result = logout_result["body"]["LogoutResult"]

if result["SessionID"] is None: # Success!
client.session_id = None
else: # Failure . . .
raise LogoutError(result=result)
1 change: 0 additions & 1 deletion membersuite_api_client/tests/test_client.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import datetime
import os
import unittest

Expand Down
27 changes: 27 additions & 0 deletions membersuite_api_client/tests/test_exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import unittest

from ..exceptions import ExecuteMSQLError
from ..utils import (get_new_client,
submit_msql_query)


client = get_new_client()


class MemberSuiteAPIErrorTestCase(unittest.TestCase):

@classmethod
def setUpClass(cls):
client.request_session()
try:
submit_msql_query(query="SELECT OBJECT() FROM BOB",
client=client)
except ExecuteMSQLError as exc:
cls.exc = exc

def test_get_concierge_error(self):
concierge_error = self.exc.get_concierge_error()
self.assertTrue(concierge_error is not None)

def test___str__(self):
self.assertTrue(str(self.exc) > '')
3 changes: 3 additions & 0 deletions membersuite_api_client/tests/test_memberships.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import unittest

from .base import BaseTestCase
from ..memberships.services import MembershipService
from ..memberships.models import Membership, MembershipProduct
Expand All @@ -9,6 +11,7 @@ def setUp(self):
super(MembershipServiceTestCase, self).setUp()
self.service = MembershipService(self.client)

@unittest.skip("Need an Organization ID for a non-member org")
def test_get_membership_for_org(self):
"""
Get membership info for a test org
Expand Down
Loading