Module upwork.client
Expand source code
# Licensed under the Upwork's API Terms of Use;
# you may not use this file except in compliance with the Terms.
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Author:: Maksym Novozhylov (mnovozhilov@upwork.com)
# Copyright:: Copyright 2020(c) Upwork.com
# License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html
from . import upwork
from oauthlib.oauth2 import BackendApplicationClient
from requests_oauthlib import OAuth2Session # type: ignore
from urllib.parse import parse_qsl, urlencode
class Client(object):
"""API client for OAuth2 authorization
*Parameters:*
:config: An instance of upwork.Config class, which contains the configuration keys and tokens
"""
__data_format = "json"
__overload_var = "http_method"
__uri_auth = "/ab/account-security/oauth2/authorize"
__uri_rtoken = "/v3/oauth2/token"
__uri_atoken = "/v3/oauth2/token"
epoint = upwork.DEFAULT_EPOINT
def __init__(self, config):
self.config = config
self.config.tenant_id = None
try:
# token is known, use it
self.__oauth = OAuth2Session(
self.config.client_id,
token=self.config.token,
auto_refresh_url=full_url(self.__uri_rtoken, upwork.DEFAULT_EPOINT),
auto_refresh_kwargs={
"client_id": self.config.client_id,
"client_secret": self.config.client_secret,
},
token_updater=self.refresh_config_from_access_token,
)
except AttributeError as e:
if self.config.grant_type == "client_credentials":
client = BackendApplicationClient(client_id=self.config.client_id)
self.__oauth = OAuth2Session(
client=client
)
else:
# start from authorization step
self.__oauth = OAuth2Session(
self.config.client_id, redirect_uri=self.config.redirect_uri
)
def get_authorization_url(self):
"""Get authorization URL
:param redirect_uri: (Default value = None)
"""
return self.__oauth.authorization_url(
"{0}{1}".format(upwork.BASE_HOST, self.__uri_auth)
)
def get_access_token(self, authorization_response=None):
"""Finish auth process and get access token
:param authorization_response:
"""
self.config.token = self.__oauth.fetch_token(
full_url(self.__uri_atoken, upwork.DEFAULT_EPOINT),
authorization_response=authorization_response,
client_secret=self.config.client_secret,
)
return self.config.token
def refresh_config_from_access_token(self, token):
"""Callback from OAuth2 client which will refresh config with actual data"""
self.config.token = token
def set_org_uid_header(self, tenant_id):
"""Configure X-Upwork-API-TenantId header"""
self.config.tenant_id = tenant_id
def get_actual_config(self):
"""Get actual client config"""
return self.config
def get(self, uri, params=None):
"""Execute GET request
:param uri:
:param params: (Default value = None)
"""
return self.send_request(uri, "get", params)
def post(self, uri, params=None):
"""Execute POST request
:param uri:
:param params: (Default value = None)
"""
return self.send_request(uri, "post", params)
def put(self, uri, params=None):
"""Execute PUT request
:param uri:
:param params: (Default value = None)
"""
return self.send_request(uri, "put", params)
def delete(self, uri, params=None):
"""Execute DELETE request
:param uri:
:param params: (Default value = None)
"""
return self.send_request(uri, "delete", params)
def send_request(self, uri, method="get", params={}):
"""Send request
:param uri:
:param method: (Default value = 'get')
:param params: (Default value = {})
"""
# delete does not support passing the parameters
if method == "delete":
params[self.__overload_var] = method
url = full_url(get_uri_with_format(uri, self.epoint), self.epoint)
if method == "get":
r = self.__oauth.get(url, params=params)
elif method == "put":
headers = {"Content-type": "application/json"}
r = self.__oauth.put(url, json=params, headers=headers)
elif method in {"post", "delete"}:
headers = {"Content-type": "application/json"}
if self.epoint == "graphql" and self.config.tenant_id:
headers["X-Upwork-API-TenantId"] = self.config.tenant_id
r = self.__oauth.post(url, json=params, headers=headers)
else:
raise ValueError(
'Do not know how to handle http method "{0}"'.format(method)
)
return r.json()
"""
"""
def full_url(uri, epoint=None):
"""Get full URL
:param uri:
:param epoint: (Default value = None)
"""
if epoint == "graphql":
return upwork.GQL_EPOINT
if not epoint:
epoint = upwork.DEFAULT_EPOINT
return "{0}/{1}{2}".format(upwork.BASE_HOST, epoint, uri)
def get_uri_with_format(uri, epoint):
"""Get URI with format ending
:param uri:
:param epoint:
"""
if epoint == upwork.DEFAULT_EPOINT:
uri += ".json"
return uri
Functions
def full_url(uri, epoint=None)
-
Get full URL
:param uri: :param epoint: (Default value = None)
Expand source code
def full_url(uri, epoint=None): """Get full URL :param uri: :param epoint: (Default value = None) """ if epoint == "graphql": return upwork.GQL_EPOINT if not epoint: epoint = upwork.DEFAULT_EPOINT return "{0}/{1}{2}".format(upwork.BASE_HOST, epoint, uri)
def get_uri_with_format(uri, epoint)
-
Get URI with format ending
:param uri: :param epoint:
Expand source code
def get_uri_with_format(uri, epoint): """Get URI with format ending :param uri: :param epoint: """ if epoint == upwork.DEFAULT_EPOINT: uri += ".json" return uri
Classes
class Client (config)
-
API client for OAuth2 authorization
Parameters: :config: An instance of upwork.Config class, which contains the configuration keys and tokens
Expand source code
class Client(object): """API client for OAuth2 authorization *Parameters:* :config: An instance of upwork.Config class, which contains the configuration keys and tokens """ __data_format = "json" __overload_var = "http_method" __uri_auth = "/ab/account-security/oauth2/authorize" __uri_rtoken = "/v3/oauth2/token" __uri_atoken = "/v3/oauth2/token" epoint = upwork.DEFAULT_EPOINT def __init__(self, config): self.config = config self.config.tenant_id = None try: # token is known, use it self.__oauth = OAuth2Session( self.config.client_id, token=self.config.token, auto_refresh_url=full_url(self.__uri_rtoken, upwork.DEFAULT_EPOINT), auto_refresh_kwargs={ "client_id": self.config.client_id, "client_secret": self.config.client_secret, }, token_updater=self.refresh_config_from_access_token, ) except AttributeError as e: if self.config.grant_type == "client_credentials": client = BackendApplicationClient(client_id=self.config.client_id) self.__oauth = OAuth2Session( client=client ) else: # start from authorization step self.__oauth = OAuth2Session( self.config.client_id, redirect_uri=self.config.redirect_uri ) def get_authorization_url(self): """Get authorization URL :param redirect_uri: (Default value = None) """ return self.__oauth.authorization_url( "{0}{1}".format(upwork.BASE_HOST, self.__uri_auth) ) def get_access_token(self, authorization_response=None): """Finish auth process and get access token :param authorization_response: """ self.config.token = self.__oauth.fetch_token( full_url(self.__uri_atoken, upwork.DEFAULT_EPOINT), authorization_response=authorization_response, client_secret=self.config.client_secret, ) return self.config.token def refresh_config_from_access_token(self, token): """Callback from OAuth2 client which will refresh config with actual data""" self.config.token = token def set_org_uid_header(self, tenant_id): """Configure X-Upwork-API-TenantId header""" self.config.tenant_id = tenant_id def get_actual_config(self): """Get actual client config""" return self.config def get(self, uri, params=None): """Execute GET request :param uri: :param params: (Default value = None) """ return self.send_request(uri, "get", params) def post(self, uri, params=None): """Execute POST request :param uri: :param params: (Default value = None) """ return self.send_request(uri, "post", params) def put(self, uri, params=None): """Execute PUT request :param uri: :param params: (Default value = None) """ return self.send_request(uri, "put", params) def delete(self, uri, params=None): """Execute DELETE request :param uri: :param params: (Default value = None) """ return self.send_request(uri, "delete", params) def send_request(self, uri, method="get", params={}): """Send request :param uri: :param method: (Default value = 'get') :param params: (Default value = {}) """ # delete does not support passing the parameters if method == "delete": params[self.__overload_var] = method url = full_url(get_uri_with_format(uri, self.epoint), self.epoint) if method == "get": r = self.__oauth.get(url, params=params) elif method == "put": headers = {"Content-type": "application/json"} r = self.__oauth.put(url, json=params, headers=headers) elif method in {"post", "delete"}: headers = {"Content-type": "application/json"} if self.epoint == "graphql" and self.config.tenant_id: headers["X-Upwork-API-TenantId"] = self.config.tenant_id r = self.__oauth.post(url, json=params, headers=headers) else: raise ValueError( 'Do not know how to handle http method "{0}"'.format(method) ) return r.json()
Class variables
var epoint
Methods
def delete(self, uri, params=None)
-
Execute DELETE request
:param uri: :param params: (Default value = None)
Expand source code
def delete(self, uri, params=None): """Execute DELETE request :param uri: :param params: (Default value = None) """ return self.send_request(uri, "delete", params)
def get(self, uri, params=None)
-
Execute GET request
:param uri: :param params: (Default value = None)
Expand source code
def get(self, uri, params=None): """Execute GET request :param uri: :param params: (Default value = None) """ return self.send_request(uri, "get", params)
def get_access_token(self, authorization_response=None)
-
Finish auth process and get access token
:param authorization_response:
Expand source code
def get_access_token(self, authorization_response=None): """Finish auth process and get access token :param authorization_response: """ self.config.token = self.__oauth.fetch_token( full_url(self.__uri_atoken, upwork.DEFAULT_EPOINT), authorization_response=authorization_response, client_secret=self.config.client_secret, ) return self.config.token
def get_actual_config(self)
-
Get actual client config
Expand source code
def get_actual_config(self): """Get actual client config""" return self.config
-
Get authorization URL
:param redirect_uri: (Default value = None)
Expand source code
def get_authorization_url(self): """Get authorization URL :param redirect_uri: (Default value = None) """ return self.__oauth.authorization_url( "{0}{1}".format(upwork.BASE_HOST, self.__uri_auth) )
def post(self, uri, params=None)
-
Execute POST request
:param uri: :param params: (Default value = None)
Expand source code
def post(self, uri, params=None): """Execute POST request :param uri: :param params: (Default value = None) """ return self.send_request(uri, "post", params)
def put(self, uri, params=None)
-
Execute PUT request
:param uri: :param params: (Default value = None)
Expand source code
def put(self, uri, params=None): """Execute PUT request :param uri: :param params: (Default value = None) """ return self.send_request(uri, "put", params)
def refresh_config_from_access_token(self, token)
-
Callback from OAuth2 client which will refresh config with actual data
Expand source code
def refresh_config_from_access_token(self, token): """Callback from OAuth2 client which will refresh config with actual data""" self.config.token = token
def send_request(self, uri, method='get', params={})
-
Send request
:param uri: :param method: (Default value = 'get') :param params: (Default value = {})
Expand source code
def send_request(self, uri, method="get", params={}): """Send request :param uri: :param method: (Default value = 'get') :param params: (Default value = {}) """ # delete does not support passing the parameters if method == "delete": params[self.__overload_var] = method url = full_url(get_uri_with_format(uri, self.epoint), self.epoint) if method == "get": r = self.__oauth.get(url, params=params) elif method == "put": headers = {"Content-type": "application/json"} r = self.__oauth.put(url, json=params, headers=headers) elif method in {"post", "delete"}: headers = {"Content-type": "application/json"} if self.epoint == "graphql" and self.config.tenant_id: headers["X-Upwork-API-TenantId"] = self.config.tenant_id r = self.__oauth.post(url, json=params, headers=headers) else: raise ValueError( 'Do not know how to handle http method "{0}"'.format(method) ) return r.json()
def set_org_uid_header(self, tenant_id)
-
Configure X-Upwork-API-TenantId header
Expand source code
def set_org_uid_header(self, tenant_id): """Configure X-Upwork-API-TenantId header""" self.config.tenant_id = tenant_id