def __call__(self, request): # According to RFC2617 the username and password are *TEXT, which # RFC2616 says may contain characters from outside of ISO-8859-1 if # they are MIME-encoded. Our first approach was to assume latin-1 in # username and password, but practice has proved us wrong (services # like Zendesk allow non-latin-1 characters in both, which are used # in basic auth for their API). To be as compatible as possible, # allow unicode in username and password, but keep resulting base64 # in latin-1. auth = port.to_u('{0}:{1}').format(port.to_u(self.username), port.to_u(self.password)) encoded = port.to_u(base64.b64encode(port.to_b(auth)), 'latin-1') header = 'Basic {0}'.format(encoded) request.headers['Authorization'] = header
def expect(self, uri, params_expected=None, subdomain=None, b64params=()): if not subdomain: domain = 'mixpanel.com' else: domain = '{0}.mixpanel.com'.format(subdomain) if subdomain != 'api': uri = 'api/2.0/{0}'.format(uri) self.assertEqual(self.executor.request.uri, 'http://{0}/{1}'.format(domain, uri)) if not params_expected: return params_used = self.executor.request.params.copy() if 'api_key' in params_used: params_expected['api_key'] = 'api-key' params_used.pop('sig', None) params_used.pop('expire', None) for name in b64params: decoded = base64.b64decode(params_used[name]) params_used[name] = json.loads(port.to_u(decoded)) self.assertEqual(params_used, params_expected)
def __init__(self, subdomain, username=None, password=None, access_token=None): """ Create a Zendesk service. :var subdomain: The account-specific part of the Zendesk domain, for instance use `mycompany` if your Zendesk domain is `mycompany.zendesk.com`. :vartype subdomain: str :var username: The email of the authenticated agent. Use `[email protected]/token` for token-based authentication. :vartype username: str :var password: The password of the authenticated agent, or an API token if using token-based authentication. :vartype password: str :var access_token: An OAuth Access token. Username and password are not required if the OAuth Access token is provided. :vartype access_token: str """ tmpl = '{0}.zendesk.com/api/v2' self.apiroot = http.quote_any(tmpl.format(port.to_u(subdomain))) self.apiroot = 'https://' + self.apiroot if access_token: self.access_token = access_token self.add_filter(self.add_authorization) else: self.add_filter(auth.BasicAuth(username, password)) self.add_filter(self.use_json)
def __init__(self, subdomain, api_key, api_secret=None, access_token=None, access_token_secret=None): """ Create a Desk service. :var subdomain: The account-specific part of the Desk domain, for instance use `mycompany` if your Desk domain is `mycompany.desk.com`. :vartype subdomain: str :var api_key: The API key. :vartype api_key: str :var api_secret: API secret. :vartype api_secret: str :var access_token: OAuth 1.0a access token. :vartype access_token: str :var access_token_secret: OAuth 1.0a access token secret. requests. :vartype access_token_secret: str """ tmpl = '{0}.desk.com/api/v1' self.apiroot = http.quote_any(tmpl.format(port.to_u(subdomain))) self.apiroot = 'https://' + self.apiroot self.oauth = auth.OAuth(access_token, access_token_secret, api_key, api_secret) self.add_filter(self.use_json) # authenticate has to be the last filter, because anything that # modifies the request after it's signed will make the signature # invalid! self.add_filter(self.authenticate)
def get(self): """ Fetch information about the subdomain. """ url = '{0}/subdomains/{1}'.format(self.parent.get_url(), port.to_u(self.object_id)) return http.Request('GET', url), parsers.parse_json
def _make_track_import_params(self, event, properties, ip, test): if not self.token: raise InsufficientSettings('token is required for this method') if properties is None: properties = {} properties['token'] = self.token properties = dict((port.to_u(key), self.serialize_property(value)) for key, value in properties.items()) params = base.get_params(('ip', 'test'), locals(), resources.serialize_param) data = {'event': port.to_u(event), 'properties': properties} params['data'] = base64.b64encode(port.to_b(json.dumps(data))) return params
def __init__(self, api_key): """ Create a MixRank service. :var api_key: The MixRank API key. :vartype api_key: str """ self.apiroot = 'http://api.mixrank.com/v2/json/' + port.to_u(api_key)
def serialize(prefix, value): """ Recursive helper for serialize_param. """ if isinstance(value, dict): # serializing a dictionary, use prefix[key] as the prefix, recurse for # all dict items and flatten the result return chain.from_iterable( (serialize('{0}[{1}]'.format(port.to_u(prefix), port.to_u(key)), val)for key, val in value.items())) elif isinstance(value, list): # serializing a list, use prefix[i] as the prefix, recurse for # all items and flatten the result return chain.from_iterable( (serialize('{0}[{1}]'.format(port.to_u(prefix), port.to_u(num)), val) for num, val in enumerate(value))) elif isinstance(value, bool): # serializing a boolean, take the prefix as-is and serialize the value # to string return ((port.to_u(prefix), 'true' if value else 'false'), ) else: # anything else, just use the prefix and value as-is return ((port.to_u(prefix), port.to_u(value)), )
def element_for_value(obj, parent): for key, value in obj.items(): if isinstance(value, dict): node = etree.SubElement(parent, key) element_for_value(value, node) elif isinstance(value, list): if not value: node = etree.SubElement(parent, key) for item in value: node = etree.SubElement(parent, key) element_for_value(item, node) else: if key.startswith("@"): parent.set(key.lstrip("@"), value) else: node = etree.SubElement(parent, key) node.text = port.to_u(value) if value is not None else port.to_u("")
def element_for_value(obj, parent): for key, value in obj.items(): if isinstance(value, dict): node = etree.SubElement(parent, key) element_for_value(value, node) elif isinstance(value, list): if not value: node = etree.SubElement(parent, key) for item in value: node = etree.SubElement(parent, key) element_for_value(item, node) else: if key.startswith('@'): parent.set(key.lstrip('@'), value) else: node = etree.SubElement(parent, key) node.text = (port.to_u(value) if value is not None else port.to_u(''))
def serialize_property(self, value): """ Serialize a Mixpanel property value. According to https://mixpanel.com/docs/properties-or-segments/property-data-types the allowed types are string, numeric, boolean, date (represented as a string) and list. """ if isinstance(value, self.LITERAL_PROPERTY_TYPES): return value return port.to_u(value)
def sign_request(self, request): if getattr(request, 'nosign', False): return expires = str(calendar.timegm(self.timesource()) + self.EXPIRES) to_sign = port.to_b(self.access_id + '\n' + expires) signature = hmac.new(port.to_b(self.secret_key), to_sign, hashlib.sha1).digest() request.params['AccessID'] = self.access_id request.params['Expires'] = expires request.params['Signature'] = port.to_u(base64.b64encode(signature))
def track(self, event, properties=None, ip=False, test=False): """ Track an event. Upstream documentation: {0} :var event: The name of the event. :vartype event: str :var properties: The event's properties, your access token will be inserted into it automatically. :vartype properties: dict :var ip: Should Mixpanel automatically use the incoming request IP. :vartype ip: bool :var test: Use a high priority rate limited queue for testing. :vartype test: bool :return: A boolean that tells if the event has been logged. """ if not self.token: raise InsufficientSettings('token is required for this method') if properties is None: properties = {} properties['token'] = self.token properites = dict((port.to_u(key), port.to_u(value))for key, value in properties.items()) params = base.get_params(('ip', 'test'), locals(), resources.serialize_param) data = {'event': port.to_u(event), 'properties': properties} params['data'] = base64.b64encode(port.to_b(json.dumps(data))) request = http.Request('GET', 'http://api.mixpanel.com/track/', params) request.nosign = True return request, resources.parse_boolean
def track(self, event, properties=None, ip=False, test=False): """ Track an event. Upstream documentation: {0} :var event: The name of the event. :vartype event: str :var properties: The event's properties, your access token will be inserted into it automatically. :vartype properties: dict :var ip: Should Mixpanel automatically use the incoming request IP. :vartype ip: bool :var test: Use a high priority rate limited queue for testing. :vartype test: bool :return: A boolean that tells if the event has been logged. """ if not self.token: raise InsufficientSettings('token is required for this method') if properties is None: properties = {} properties['token'] = self.token properites = dict((port.to_u(key), port.to_u(value)) for key, value in properties.items()) params = base.get_params(('ip', 'test'), locals(), resources.serialize_param) data = {'event': port.to_u(event), 'properties': properties} params['data'] = base64.b64encode(port.to_b(json.dumps(data))) request = http.Request('GET', 'http://api.mixpanel.com/track/', params) request.nosign = True return request, resources.parse_boolean
def __init__(self, apikey, label): """ Get a datasource resource. :var apikey: Your API key. :vartype apikey: str :var label: data source label :vartype label: str """ self.url_tmpl = port.to_u('https://{0}.ducksboard.com/values/{1}') self.label = label self.add_filter(auth.BasicAuth(apikey, 'x')) self.add_filter(self.use_json)
def __init__(self, api_key): """ Create a Mailchimp service. :var api_key: The API key including the region, for instance `8ac789caf98879caf897a678fa76daf-us2`. :vartype api_key: str """ self.api_key, dc = port.to_u(api_key).split('-') tmpl = '{0}.api.mailchimp.com/1.3/' self.apiroot = http.quote_any(tmpl.format(dc)) self.apiroot = 'https://' + self.apiroot self.add_filter(self.add_api_root) self.add_filter(self.add_params)
def __init__(self, subdomain, api_key, api_secret=None, access_token=None, access_token_secret=None): """ Create a UserVoice service. :var subdomain: The account-specific part of the UserVoice domain, for instance use `mycompany` if your UserVoice domain is `mycompany.uservoice.com`. :vartype subdomain: str :var api_key: The API key. :vartype api_key: str :var api_secret: Optional API secret. If you leave this as None, all requests will be made as unauthenticated requests. :vartype api_secret: str or None :var access_token: Optional OAuth 1.0a access token. If you leave this as None, all requests be made as unauthenticated requests. :vartype access_token: str or None :var access_token_secret: Optional OAuth 1.0a access token secret. If you leave this as None, all requests be made as unauthenticated requests. :vartype access_token_secret: str or None """ self.api_key = api_key self.oauth = None if api_secret and access_token and access_token_secret: self.oauth = auth.OAuth1a(access_token, access_token_secret, api_key, api_secret) tmpl = '{0}.uservoice.com/api/v1' self.apiroot = http.quote_any(tmpl.format(port.to_u(subdomain))) self.apiroot = 'http://' + self.apiroot self.add_filter(self.use_json) self.add_filter(self.serialize_flatten) # authenticate has to be the last filter, because anything that # modifies the request after it's signed will make the signature # invalid! self.add_filter(self.authenticate)
def __init__(self, subdomain, api_key): """ Create a CartoDB service. :var subdomain: The account-specific part of the CartoDB domain, for instance use `mycompany` if your CartpDB domain is `mycompany.cartodb.com`. :vartype subdomain: str :var api_key: The API key. :vartype api_key: str """ tmpl = '{0}.cartodb.com/api' self.apiroot = http.quote_any(tmpl.format(port.to_u(subdomain))) self.apiroot = 'https://' + self.apiroot self.api_key = api_key self.add_filter(self.add_api_key)
def sign_request(self, request): if getattr(request, "nosign", False): return if not self.api_key or not self.api_secret: raise InsufficientSettings("api_key and api_secret are required " "for this method") request.params["api_key"] = self.api_key if self.USE_EXPIRE: request.params["expire"] = calendar.timegm(time.gmtime()) + 600 to_hash = "".join( "{0}={1}".format(key, port.to_u(request.params[key])) for key in sorted(request.params.keys()) ) md5 = hashlib.md5() md5.update(port.to_b(to_hash)) md5.update(port.to_b(self.api_secret)) request.params["sig"] = md5.hexdigest()
def sign_request(self, request): if getattr(request, 'nosign', False): return if not self.api_key or not self.api_secret: raise InsufficientSettings('api_key and api_secret are required ' 'for this method') request.params['api_key'] = self.api_key if self.USE_EXPIRE: request.params['expire'] = calendar.timegm(time.gmtime()) + 600 to_hash = ''.join('{0}={1}'.format(key, port.to_u(request.params[key])) for key in sorted(request.params.keys())) md5 = hashlib.md5() md5.update(port.to_b(to_hash)) md5.update(port.to_b(self.api_secret)) request.params['sig'] = md5.hexdigest()
def __init__(self, subdomain, username, password): """ Create a Zendesk service. :var subdomain: The account-specific part of the Zendesk domain, for instance use `mycompany` if your Zendesk domain is `mycompany.zendesk.com`. :vartype subdomain: str :var username: The email of the authenticated agent. :vartype username: str :var password: The password of the authenticated agent. :vartype password: str """ tmpl = '{0}.zendesk.com/api/v2' self.apiroot = http.quote_any(tmpl.format(port.to_u(subdomain))) self.apiroot = 'https://' + self.apiroot self.add_filter(auth.BasicAuth(username, password)) self.add_filter(self.use_json)
def __init__(self, subdomain, api_key, api_secret=None, access_token=None, access_token_secret=None): """ Create a Desk service. :var subdomain: The account-specific part of the Desk domain, for instance use `mycompany` if your Desk domain is `mycompany.desk.com`, or the full domain if using Desk whitelabel, for instance `support.mycompany.com`. If the parameter contains a dot, it is treated as a full domain, otherwise as a subdomain. :vartype subdomain: str :var api_key: The API key. :vartype api_key: str :var api_secret: API secret. :vartype api_secret: str :var access_token: OAuth 1.0a access token. :vartype access_token: str :var access_token_secret: OAuth 1.0a access token secret. requests. :vartype access_token_secret: str """ tmpl = '{0}/api/v1' if '.' not in subdomain: subdomain += '.desk.com' self.apiroot = http.quote_any(tmpl.format(port.to_u(subdomain))) self.apiroot = 'https://' + self.apiroot self.oauth = auth.OAuth(access_token, access_token_secret, api_key, api_secret) self.add_filter(self.use_json) # authenticate has to be the last filter, because anything that # modifies the request after it's signed will make the signature # invalid! self.add_filter(self.authenticate)
def __init__(self, subdomain, api_key, api_secret=None, access_token=None, access_token_secret=None): """ Create a Desk service. :var subdomain: The account-specific part of the Desk domain, for instance use `mycompany` if your Desk domain is `mycompany.desk.com`, or the full domain if using Desk whitelabel, for instance `support.mycompany.com`. If the parameter contains a dot, it is treated as a full domain, otherwise as a subdomain. :vartype subdomain: str :var api_key: The API key. :vartype api_key: str :var api_secret: API secret. :vartype api_secret: str :var access_token: OAuth 1.0a access token. :vartype access_token: str :var access_token_secret: OAuth 1.0a access token secret. requests. :vartype access_token_secret: str """ tmpl = '{0}/api/v2' if '.' not in subdomain: subdomain += '.desk.com' self.apiroot = http.quote_any(tmpl.format(port.to_u(subdomain))) self.apiroot = 'https://' + self.apiroot self.oauth = auth.OAuth(access_token, access_token_secret, api_key, api_secret) self.add_filter(self.use_json) # authenticate has to be the last filter, because anything that # modifies the request after it's signed will make the signature # invalid! self.add_filter(self.authenticate)
def serialize(prefix, value): """ Recursive helper for serialize_param. """ if isinstance(value, dict): # serializing a dictionary, use prefix[key] as the prefix, recurse for # all dict items and flatten the result return chain.from_iterable( (serialize('{0}[{1}]'.format(port.to_u(prefix), port.to_u(key)), val) for key, val in value.items())) elif isinstance(value, list): # serializing a list, use prefix[i] as the prefix, recurse for # all items and flatten the result return chain.from_iterable( (serialize('{0}[{1}]'.format(port.to_u(prefix), port.to_u(num)), val) for num, val in enumerate(value))) elif isinstance(value, bool): # serializing a boolean, take the prefix as-is and serialize the value # to string return ((port.to_u(prefix), 'true' if value else 'false'), ) else: # anything else, just use the prefix and value as-is return ((port.to_u(prefix), port.to_u(value)), )
def serialize_param(val): if isinstance(val, bool): return '1' if val else '0' if isinstance(val, list): return json.dumps([port.to_u(v) for v in val]) return val
def get_url(self, api): return self.url_tmpl.format(api, port.to_u(self.label))