Update MembershipWorks module to v2 of the API, where available

still stuck using the v1 csv endpoint, haven't found an easy
alternative and it hasn't been changed yet for their v2
This commit is contained in:
Adam Goldsmith 2020-10-27 21:05:21 -04:00
parent cfccc433dd
commit 3849aca918

View File

@ -3,7 +3,7 @@ from io import StringIO
import requests import requests
BASE_URL = "https://api.membershipworks.com/v1/" BASE_URL = "https://api.membershipworks.com"
# extracted from `SF._is.crm` in https://cdn.membershipworks.com/all.js # extracted from `SF._is.crm` in https://cdn.membershipworks.com/all.js
CRM = { CRM = {
@ -72,21 +72,30 @@ class MembershipWorksRemoteError(Exception):
class MembershipWorks: class MembershipWorks:
def __init__(self): def __init__(self):
self.sess = requests.Session()
self.org_info = None self.org_info = None
self.auth_token = None self.auth_token = None
self.org_num = None self.org_num = None
def login(self, username, password): def login(self, username, password):
"""Authenticate against the membershipworks api""" """Authenticate against the membershipworks api"""
r = requests.post( r = self.sess.post(
BASE_URL + "usr", BASE_URL + "/v2/account/session",
data={"_st": "all", "eml": username, "org": "10000", "pwd": password}, data={"eml": username, "pwd": password},
headers={"X-Org": "10000"},
) )
if r.status_code != 200 or "SF" not in r.json(): if r.status_code != 200 or "SF" not in r.json():
raise MembershipWorksRemoteError("login", r) raise MembershipWorksRemoteError("login", r)
self.org_info = r.json() self.org_info = r.json()
self.auth_token = self.org_info["SF"] self.auth_token = self.org_info["SF"]
self.org_num = self.org_info["org"] self.org_num = self.org_info["org"]
self.sess.headers.update(
{
"X-Org": str(self.org_num),
"X-Role": "admin",
"Authorization": "Bearer " + self.auth_token,
}
)
def _inject_auth(self, kwargs): def _inject_auth(self, kwargs):
# TODO: should probably be a decorator or something # TODO: should probably be a decorator or something
@ -97,12 +106,12 @@ class MembershipWorks:
kwargs["params"] = {} kwargs["params"] = {}
kwargs["params"]["SF"] = self.auth_token kwargs["params"]["SF"] = self.auth_token
def _get(self, *args, **kwargs): def _get_v1(self, *args, **kwargs):
self._inject_auth(kwargs) self._inject_auth(kwargs)
# TODO: should probably do some error handling in here # TODO: should probably do some error handling in here
return requests.get(*args, **kwargs) return requests.get(*args, **kwargs)
def _post(self, *args, **kwargs): def _post_v1(self, *args, **kwargs):
self._inject_auth(kwargs) self._inject_auth(kwargs)
# TODO: should probably do some error handling in here # TODO: should probably do some error handling in here
return requests.post(*args, **kwargs) return requests.post(*args, **kwargs)
@ -141,32 +150,30 @@ class MembershipWorks:
for dek in self.org_info["dek"]: for dek in self.org_info["dek"]:
# TODO: there must be a better way. this is stupid # TODO: there must be a better way. this is stupid
if dek["dek"] == 1: if dek["dek"] == 1:
ret["folders"][dek["lbl"]] = dek["_id"] ret["folders"][dek["lbl"]] = dek["did"]
elif "cur" in dek: elif "cur" in dek:
ret["levels"][dek["lbl"]] = dek["_id"] ret["levels"][dek["lbl"]] = dek["did"]
elif "mux" in dek: elif "mux" in dek:
ret["addons"][dek["lbl"]] = dek["_id"] ret["addons"][dek["lbl"]] = dek["did"]
else: else:
ret["labels"][dek["lbl"]] = dek["_id"] ret["labels"][dek["lbl"]] = dek["did"]
return ret return ret
def get_member_ids(self, folders): def get_member_ids(self, folders):
folder_map = self._parse_flags()["folders"] folder_map = self._parse_flags()["folders"]
r = self._get( r = self.sess.get(
BASE_URL + "ylp", BASE_URL + "/v2/accounts",
params={ params={"dek": ",".join([folder_map[f] for f in folders])},
"lbl": ",".join([folder_map[f] for f in folders]),
"org": self.org_num,
"var": "_id,nam,ctc",
},
) )
if r.status_code != 200 or "usr" not in r.json(): if r.status_code != 200 or "usr" not in r.json():
raise MembershipWorksRemoteError("user listing", r) raise MembershipWorksRemoteError("user listing", r)
# get list of member ID matching the search # get list of member ID matching the search
return [user["uid"] for user in r.json()["usr"]] # dedup with set() to work around people with alt uids
# TODO: figure out why people have alt uids
return set(user["uid"] for user in r.json()["usr"])
# TODO: has issues with aliasing header names: # TODO: has issues with aliasing header names:
# ex: "Personal Studio Space" Label vs Membership Addon/Field # ex: "Personal Studio Space" Label vs Membership Addon/Field
@ -179,8 +186,8 @@ class MembershipWorks:
# get members CSV # get members CSV
# TODO: maybe can just use previous get instead? would return JSON # TODO: maybe can just use previous get instead? would return JSON
r = self._post( r = self._post_v1(
BASE_URL + "csv", BASE_URL + "/v1/csv",
data={ data={
"_rt": "946702800", # unknown "_rt": "946702800", # unknown
"mux": "", # unknown "mux": "", # unknown
@ -200,8 +207,8 @@ class MembershipWorks:
json gets a different version of the transactions list, json gets a different version of the transactions list,
which contains a different set information which contains a different set information
""" """
r = self._get( r = self._get_v1(
BASE_URL + "csv", BASE_URL + "/v1/csv",
params={ params={
"crm": "12,13,14,18,19", # transaction types, see CRM "crm": "12,13,14,18,19", # transaction types, see CRM
**({"txl": ""} if json else {}), **({"txl": ""} if json else {}),