Skip to content

Commit 9908cb7

Browse files
author
derwentx
committed
added more tests cases for 3leg
1 parent 1dea17e commit 9908cb7

File tree

5 files changed

+169
-31
lines changed

5 files changed

+169
-31
lines changed

README.rst

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ Roadmap
1717

1818
- [x] Create initial fork
1919
- [x] Implement 3-legged OAuth on Wordpress client
20-
- [x] Better local storage of OAuth creds to stop unnecessary API keys being generated
21-
- [ ] Implement iterator for conveniant access to API items
20+
- [x] Better local storage of OAuth credentials to stop unnecessary API keys being generated
21+
- [ ] Support easy image upload to WC Api
22+
- [ ] Better handling of timeouts with a back-off
23+
- [ ] Implement iterator for convenient access to API items
2224

2325
Requirements
2426
------------
@@ -138,7 +140,8 @@ Setup for the new WP REST API integration (WooCommerce 2.6 or later):
138140
consumer_key="ck_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
139141
consumer_secret="cs_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
140142
api="wp-json",
141-
version="wc/v1"
143+
version="wc/v2",
144+
callback='http://127.0.0.1/oauth1_callback'
142145
)
143146
144147
Options
@@ -165,6 +168,8 @@ Options
165168
+-----------------------+-------------+----------+------------------------------------------------------------------------------------------------------------------+
166169
| ``query_string_auth`` | ``bool`` | no | Use query string for Basic Authentication when ``True`` and using HTTPS, default is ``False`` which uses header |
167170
+-----------------------+-------------+----------+------------------------------------------------------------------------------------------------------------------+
171+
| ``oauth1a_3leg`` | ``string`` | no | use oauth1a 3-legged authentication |
172+
+-----------------------+-------------+----------+------------------------------------------------------------------------------------------------------------------+
168173
| ``creds_store`` | ``string`` | no | JSON file where oauth verifier is stored (only used with OAuth_3Leg) |
169174
+-----------------------+-------------+----------+------------------------------------------------------------------------------------------------------------------+
170175

tests.py

Lines changed: 72 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -373,19 +373,21 @@ def setUp(self):
373373
consumer_secret=self.consumer_secret,
374374
basic_auth=True,
375375
api=self.api_name,
376-
version=self.api_ver
376+
version=self.api_ver,
377+
query_string_auth=False,
377378
)
378379

379380
def test_endpoint_url(self):
380-
basic_api_params = dict(**self.api_params)
381381
api = API(
382-
**basic_api_params
382+
**self.api_params
383383
)
384384
endpoint_url = api.requester.endpoint_url(self.endpoint)
385385
endpoint_url = api.auth.get_auth_url(endpoint_url, 'GET')
386386
self.assertEqual(
387387
endpoint_url,
388-
UrlUtils.join_components([self.base_url, self.api_name, self.api_ver, self.endpoint])
388+
UrlUtils.join_components([
389+
self.base_url, self.api_name, self.api_ver, self.endpoint
390+
])
389391
)
390392

391393
def test_query_string_endpoint_url(self):
@@ -590,16 +592,48 @@ def test_get_sign_key(self):
590592
)
591593

592594
def test_flatten_params(self):
593-
flattened_params = UrlUtils.flatten_params(self.twitter_params_raw)
594-
expected_flattened_params = self.twitter_param_string
595-
self.assertEqual(flattened_params, expected_flattened_params)
595+
self.assertEqual(
596+
UrlUtils.flatten_params(self.twitter_params_raw),
597+
self.twitter_param_string
598+
)
599+
600+
def test_sorted_params(self):
601+
# Example given in oauth.net:
602+
oauthnet_example_sorted = [
603+
('a', '1'),
604+
('c', 'hi%%20there'),
605+
('f', '25'),
606+
('f', '50'),
607+
('f', 'a'),
608+
('z', 'p'),
609+
('z', 't')
610+
]
611+
612+
oauthnet_example = copy(oauthnet_example_sorted)
613+
random.shuffle(oauthnet_example)
614+
615+
# oauthnet_example_sorted = [
616+
# ('a', '1'),
617+
# ('c', 'hi%%20there'),
618+
# ('f', '25'),
619+
# ('z', 'p'),
620+
# ]
621+
622+
self.assertEqual(
623+
UrlUtils.sorted_params(oauthnet_example),
624+
oauthnet_example_sorted
625+
)
596626

597-
twitter_base_string = OAuth.get_signature_base_string(
627+
def test_get_signature_base_string(self):
628+
twitter_param_string = OAuth.get_signature_base_string(
598629
self.twitter_method,
599630
self.twitter_params_raw,
600631
self.twitter_target_url
601632
)
602-
self.assertEqual(twitter_base_string, self.twitter_signature_base_string)
633+
self.assertEqual(
634+
twitter_param_string,
635+
self.twitter_signature_base_string
636+
)
603637

604638
# @unittest.skip("changed order of parms to fit wordpress api")
605639
def test_generate_oauth_signature(self):
@@ -815,8 +849,8 @@ def setUp(self):
815849
'url':'http://localhost:18080/wptest/',
816850
'api':'wc-api',
817851
'version':'v3',
818-
'consumer_key':'ck_0297450a41484f27184d1a8a3275f9bab5b69143',
819-
'consumer_secret':'cs_68ef2cf6a708e1c6b30bfb2a38dc948b16bf46c0',
852+
'consumer_key':'ck_e1dd4a9c85f49b9685f7964a154eecb29af39d5a',
853+
'consumer_secret':'cs_8ef3e5d21f8a0c28cd7bc4643e92111a0326b6b1',
820854
}
821855

822856
def test_APIGet(self):
@@ -873,14 +907,14 @@ def test_APIPutWithSimpleQuery(self):
873907

874908
@unittest.skip("Should only work on my machine")
875909
class WCApiTestCasesNew(unittest.TestCase):
876-
""" Tests for New WC API """
910+
""" Tests for New wp-json/wc/v2 API """
877911
def setUp(self):
878912
self.api_params = {
879913
'url':'http://localhost:18080/wptest/',
880914
'api':'wp-json',
881915
'version':'wc/v2',
882-
'consumer_key':'ck_0297450a41484f27184d1a8a3275f9bab5b69143',
883-
'consumer_secret':'cs_68ef2cf6a708e1c6b30bfb2a38dc948b16bf46c0',
916+
'consumer_key':'ck_e1dd4a9c85f49b9685f7964a154eecb29af39d5a',
917+
'consumer_secret':'cs_8ef3e5d21f8a0c28cd7bc4643e92111a0326b6b1',
884918
'callback':'http://127.0.0.1/oauth1_callback',
885919
}
886920

@@ -903,17 +937,38 @@ def test_APIPutWithSimpleQuery(self):
903937
product_id = first_product['id']
904938

905939
nonce = str(random.random())
906-
response = wcapi.put('products/%s?filter%%5Blimit%%5D=5' % (product_id), {"name":str(nonce)})
940+
response = wcapi.put('products/%s?page=2&per_page=5' % (product_id), {"name":str(nonce)})
907941
request_params = UrlUtils.get_query_dict_singular(response.request.url)
908942
response_obj = response.json()
909943
self.assertEqual(response_obj['name'], str(nonce))
910944
self.assertEqual(request_params['filter[limit]'], str(5))
911945

912946
wcapi.put('products/%s' % (product_id), {"name":original_title})
913947

948+
@unittest.skip("Should only work on my machine")
949+
class WCApiTestCasesNew3Leg(unittest.TestCase):
950+
""" Tests for New wp-json/wc/v2 API with 3-leg """
951+
def setUp(self):
952+
self.api_params = {
953+
'url':'http://localhost:18080/wptest/',
954+
'api':'wp-json',
955+
'version':'wc/v2',
956+
'consumer_key':'ck_e1dd4a9c85f49b9685f7964a154eecb29af39d5a',
957+
'consumer_secret':'cs_8ef3e5d21f8a0c28cd7bc4643e92111a0326b6b1',
958+
'callback':'http://127.0.0.1/oauth1_callback',
959+
'oauth1a_3leg': True,
960+
'wp_user': 'wptest',
961+
'wp_pass':'gZ*gZk#v0t5$j#NQ@9'
962+
}
914963

915-
# def test_APIPut(self):
916-
964+
@debug_on()
965+
def test_api_get_3leg(self):
966+
wcapi = API(**self.api_params)
967+
per_page = 10
968+
response = wcapi.get('products')
969+
self.assertIn(response.status_code, [200,201])
970+
response_obj = response.json()
971+
self.assertEqual(len(response_obj), per_page)
917972

918973
# @unittest.skipIf(platform.uname()[1] != "Ich.lan", "should only work on my machine")
919974
@unittest.skip("Should only work on my machine")
@@ -941,7 +996,6 @@ def test_APIGet(self):
941996
response_obj = response.json()
942997
self.assertEqual(response_obj[0]['name'], self.api_params['wp_user'])
943998

944-
@debug_on()
945999
def test_APIGetWithSimpleQuery(self):
9461000
response = self.wpapi.get('media?page=2&per_page=2')
9471001
# print UrlUtils.beautify_response(response)

wordpress/api.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
__title__ = "wordpress-api"
88

99
# from requests import request
10+
import logging
1011
from json import dumps as jsonencode
1112
from wordpress.auth import OAuth, OAuth_3Leg, BasicAuth
1213
from wordpress.transport import API_Requests_Wrapper
@@ -16,7 +17,7 @@ class API(object):
1617
""" API Class """
1718

1819
def __init__(self, url, consumer_key, consumer_secret, **kwargs):
19-
20+
self.logger = logging.getLogger(__name__)
2021
self.requester = API_Requests_Wrapper(url=url, **kwargs)
2122

2223
auth_kwargs = dict(
@@ -163,11 +164,12 @@ def __request(self, method, endpoint, data):
163164
data=data
164165
)
165166

166-
if response.status_code not in [200, 201]:
167+
if response.status_code not in [200, 201, 202]:
167168
self.request_post_mortem(response)
168169

169170
return response
170171

172+
# TODO add kwargs option for headers
171173
def get(self, endpoint):
172174
""" Get requests """
173175
return self.__request("GET", endpoint, None)

wordpress/helpers.py

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ def filter_true(cls, seq):
5252

5353
class UrlUtils(object):
5454

55+
reg_netloc = r'(?P<hostname>[^:]+)(:(?P<port>\d+))?'
56+
5557
@classmethod
5658
def get_query_list(cls, url):
5759
""" returns the list of queries in the url """
@@ -174,7 +176,7 @@ def beautify_response(response):
174176

175177
@classmethod
176178
def remove_port(cls, url):
177-
""" Remove the port number from a URL """
179+
""" Remove the port number from a URL"""
178180

179181
urlparse_result = urlparse(url)
180182

@@ -187,10 +189,61 @@ def remove_port(cls, url):
187189
fragment=urlparse_result.fragment
188190
))
189191

192+
@classmethod
193+
def remove_default_port(cls, url, defaults=None):
194+
""" Remove the port number from a URL if it is a default port. """
195+
if defaults is None:
196+
defaults = {
197+
'http':80,
198+
'https':443
199+
}
200+
201+
urlparse_result = urlparse(url)
202+
match = re.match(
203+
cls.reg_netloc,
204+
urlparse_result.netloc
205+
)
206+
assert match, "netloc %s should match regex %s"
207+
if match.groupdict().get('port'):
208+
hostname = match.groupdict()['hostname']
209+
port = int(match.groupdict()['port'])
210+
scheme = urlparse_result.scheme.lower()
211+
212+
if defaults[scheme] == port:
213+
return urlunparse(URLParseResult(
214+
scheme=urlparse_result.scheme,
215+
netloc=hostname,
216+
path=urlparse_result.path,
217+
params=urlparse_result.params,
218+
query=urlparse_result.query,
219+
fragment=urlparse_result.fragment
220+
))
221+
return urlunparse(URLParseResult(
222+
scheme=urlparse_result.scheme,
223+
netloc=urlparse_result.netloc,
224+
path=urlparse_result.path,
225+
params=urlparse_result.params,
226+
query=urlparse_result.query,
227+
fragment=urlparse_result.fragment
228+
))
229+
230+
@classmethod
231+
def lower_scheme(cls, url):
232+
""" ensure the scheme of the url is lowercase. """
233+
urlparse_result = urlparse(url)
234+
return urlunparse(URLParseResult(
235+
scheme=urlparse_result.scheme.lower(),
236+
netloc=urlparse_result.netloc,
237+
path=urlparse_result.path,
238+
params=urlparse_result.params,
239+
query=urlparse_result.query,
240+
fragment=urlparse_result.fragment
241+
))
242+
190243
@classmethod
191244
def normalize_str(cls, string):
192245
""" Normalize string for the purposes of url query parameters. """
193-
return quote(string, '')
246+
return quote(string, '~')
194247

195248
@classmethod
196249
def normalize_params(cls, params):

wordpress/transport.py

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@
66

77
__title__ = "wordpress-requests"
88

9-
from requests import Request, Session
9+
import logging
1010
from json import dumps as jsonencode
11+
from pprint import pformat
12+
13+
from requests import Request, Session
14+
from wordpress import __default_api__, __default_api_version__, __version__
15+
from wordpress.helpers import SeqUtils, StrUtils, UrlUtils
1116

1217
try:
1318
from urllib.parse import urlencode, quote, unquote, parse_qsl, urlparse, urlunparse
@@ -17,14 +22,11 @@
1722
from urlparse import parse_qsl, urlparse, urlunparse
1823
from urlparse import ParseResult as URLParseResult
1924

20-
from wordpress import __version__
21-
from wordpress import __default_api_version__
22-
from wordpress import __default_api__
23-
from wordpress.helpers import SeqUtils, UrlUtils, StrUtils
2425

2526
class API_Requests_Wrapper(object):
2627
""" provides a wrapper for making requests that handles session info """
2728
def __init__(self, url, **kwargs):
29+
self.logger = logging.getLogger(__name__)
2830
self.url = url
2931
self.api = kwargs.get("api", __default_api__)
3032
self.api_version = kwargs.get("version", __default_api_version__)
@@ -88,9 +90,31 @@ def request(self, method, url, auth=None, params=None, data=None, **kwargs):
8890
request_kwargs['params'] = params
8991
if data is not None:
9092
request_kwargs['data'] = data
91-
return self.session.request(
93+
self.logger.debug("request_kwargs:\n%s" % pformat(request_kwargs))
94+
response = self.session.request(
9295
**request_kwargs
9396
)
97+
self.logger.debug("response_code:\n%s" % pformat(response.status_code))
98+
try:
99+
response_json = response.json()
100+
self.logger.debug("response_json:\n%s" % (pformat(response_json)[:1000]))
101+
except ValueError:
102+
response_text = response.text
103+
self.logger.debug("response_text:\n%s" % (response_text[:1000]))
104+
response_headers = {}
105+
if hasattr(response, 'headers'):
106+
response_headers = response.headers
107+
self.logger.debug("response_headers:\n%s" % pformat(response_headers))
108+
response_links = {}
109+
if hasattr(response, 'links') and response.links:
110+
response_links = response.links
111+
self.logger.debug("response_links:\n%s" % pformat(response_links))
112+
113+
114+
115+
116+
117+
return response
94118

95119
def get(self, *args, **kwargs):
96120
return self.request("GET", *args, **kwargs)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy