Thanks to visit codestin.com
Credit goes to github.com

Skip to content
This repository was archived by the owner on Mar 18, 2019. It is now read-only.

Commit cce8aae

Browse files
Add 'credentials' and 'headers' commands to client
1 parent 4b52186 commit cce8aae

File tree

5 files changed

+195
-59
lines changed

5 files changed

+195
-59
lines changed

coreapi/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ def get_default_session():
2727
return _default_session
2828

2929

30-
def get_session(credentials):
30+
def get_session(credentials=None, headers=None):
3131
return Session(
3232
codecs=[CoreJSONCodec(), HTMLCodec(), PlainTextCodec()],
33-
transports=[HTTPTransport(credentials=credentials)]
33+
transports=[HTTPTransport(credentials=credentials, headers=headers)]
3434
)
3535

3636

coreapi/commandline.py

Lines changed: 159 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
import sys
66

77

8-
class NoDocument(Exception):
9-
pass
8+
config_path = os.path.join(os.path.expanduser('~'), '.coreapi')
9+
10+
store_path = os.path.join(config_path, 'document.json')
11+
credentials_path = os.path.join(config_path, 'credentials.json')
12+
headers_path = os.path.join(config_path, 'headers.json')
1013

1114

1215
def coerce_key_types(doc, keys):
@@ -36,63 +39,44 @@ def coerce_key_types(doc, keys):
3639
return ret
3740

3841

39-
def get_credentials_path():
40-
directory = os.path.join(os.path.expanduser('~'), '.coreapi')
41-
if os.path.isfile(directory):
42-
os.remove(directory)
43-
os.mkdir(directory)
44-
elif not os.path.exists(directory):
45-
os.mkdir(directory)
46-
return os.path.join(directory, 'credentials.json')
47-
48-
49-
def get_store_path():
50-
directory = os.path.join(os.path.expanduser('~'), '.coreapi')
51-
if os.path.isfile(directory):
52-
os.remove(directory)
53-
os.mkdir(directory)
54-
elif not os.path.exists(directory):
55-
os.mkdir(directory)
56-
return os.path.join(directory, 'document.json')
42+
def get_session():
43+
credentials = get_credentials()
44+
headers = get_headers()
45+
return coreapi.get_session(credentials, headers)
5746

5847

59-
def get_session():
60-
path = get_credentials_path()
61-
if os.path.exists(path) and os.path.isfile(path):
62-
store = open(path, 'rb')
63-
credentials = json.loads(store.read())
64-
store.close()
65-
return coreapi.get_session(credentials)
66-
return coreapi.get_default_session()
48+
def read_from_store():
49+
if not os.path.exists(store_path):
50+
return None
51+
store = open(store_path, 'rb')
52+
content = store.read()
53+
store.close()
54+
return coreapi.load(content)
6755

6856

6957
def write_to_store(doc):
70-
path = get_store_path()
7158
content_type, content = coreapi.dump(doc)
72-
store = open(path, 'wb')
59+
store = open(store_path, 'wb')
7360
store.write(content)
7461
store.close()
7562

7663

77-
def read_from_store():
78-
path = get_store_path()
79-
if not os.path.exists(path):
80-
raise NoDocument()
81-
store = open(path, 'rb')
82-
content = store.read()
83-
store.close()
84-
return coreapi.load(content)
85-
86-
8764
def dump_to_console(doc):
8865
codec = coreapi.codecs.PlainTextCodec()
8966
return codec.dump(doc, colorize=True)
9067

9168

69+
# Core commands
70+
9271
@click.group(invoke_without_command=True, help='Command line client for interacting with CoreAPI services.\n\nVisit http://www.coreapi.org for more information.')
9372
@click.option('--version', is_flag=True, help='Display the package version number.')
9473
@click.pass_context
9574
def client(ctx, version):
75+
if os.path.isfile(config_path):
76+
os.remove(config_path)
77+
if not os.path.isdir(config_path):
78+
os.mkdir(config_path)
79+
9680
if ctx.invoked_subcommand is not None:
9781
return
9882

@@ -113,21 +97,18 @@ def get(url):
11397

11498
@click.command(help='Remove the current document, and any stored credentials.')
11599
def clear():
116-
path = get_store_path()
117-
if os.path.exists(path):
118-
os.remove(path)
119-
path = get_credentials_path()
120-
if os.path.exists(path):
121-
os.remove(path)
100+
if os.path.exists(store_path):
101+
os.remove(store_path)
102+
if os.path.exists(credentials_path):
103+
os.remove(credentials_path)
122104
click.echo('Cleared.')
123105

124106

125107
@click.command(help='Display the current document, or element at the given PATH.')
126108
@click.argument('path', nargs=-1)
127109
def show(path):
128-
try:
129-
doc = read_from_store()
130-
except NoDocument:
110+
doc = read_from_store()
111+
if doc is None:
131112
click.echo('No current document. Use `coreapi get` to fetch a document first.')
132113
return
133114

@@ -156,9 +137,8 @@ def action(path, param):
156137

157138
params = dict([tuple(item.split('=', 1)) for item in param])
158139

159-
try:
160-
doc = read_from_store()
161-
except NoDocument:
140+
doc = read_from_store()
141+
if doc is None:
162142
click.echo('No current document. Use `coreapi get` to fetch a document first.')
163143
return
164144

@@ -169,11 +149,138 @@ def action(path, param):
169149
write_to_store(doc)
170150

171151

152+
# Credentials
153+
154+
def get_credentials():
155+
if not os.path.isfile(credentials_path):
156+
return {}
157+
store = open(credentials_path, 'rb')
158+
credentials = json.loads(store.read())
159+
store.close()
160+
return credentials
161+
162+
163+
def set_credentials(credentials):
164+
store = open(credentials_path, 'wb')
165+
store.write(json.dumps(credentials))
166+
store.close
167+
168+
169+
@click.group(help='Configure credentials using in request "Authorization:" headers.')
170+
def credentials():
171+
pass
172+
173+
174+
@click.command(help="List stored credentials.")
175+
def credentials_show():
176+
credentials = get_credentials()
177+
if credentials:
178+
width = max([len(key) for key in credentials.keys()])
179+
fmt = '{domain:%d} "{header}"' % width
180+
181+
click.echo(click.style('Credentials', bold=True))
182+
for key, value in sorted(credentials.items()):
183+
click.echo(fmt.format(domain=key, header=value))
184+
185+
186+
@click.command(help="Add CREDENTIALS string for the given DOMAIN.")
187+
@click.argument('domain', nargs=1)
188+
@click.argument('header', nargs=1)
189+
def credentials_add(domain, header):
190+
credentials = get_credentials()
191+
credentials[domain] = header
192+
set_credentials(credentials)
193+
194+
click.echo(click.style('Added credentials', bold=True))
195+
click.echo('%s "%s"' % (domain, header))
196+
197+
198+
@click.command(help="Remove credentials for the given DOMAIN.")
199+
@click.argument('domain', nargs=1)
200+
def credentials_remove(domain):
201+
credentials = get_credentials()
202+
credentials.pop(domain, None)
203+
set_credentials(credentials)
204+
205+
click.echo(click.style('Removed credentials', bold=True))
206+
click.echo(domain)
207+
208+
209+
# Headers
210+
211+
def get_headers():
212+
if not os.path.isfile(headers_path):
213+
return {}
214+
headers_file = open(headers_path, 'rb')
215+
headers = json.loads(headers_file.read())
216+
headers_file.close()
217+
return headers
218+
219+
220+
def set_headers(headers):
221+
headers_file = open(headers_path, 'wb')
222+
headers_file.write(json.dumps(headers))
223+
headers_file.close()
224+
225+
226+
def titlecase(header):
227+
return '-'.join([word.title() for word in header.split('-')])
228+
229+
230+
@click.group(help="Configure custom request headers.")
231+
def headers():
232+
pass
233+
234+
235+
@click.command(help="List custom request headers.")
236+
def headers_show():
237+
headers = get_headers()
238+
239+
click.echo(click.style('Headers', bold=True))
240+
for key, value in sorted(headers.items()):
241+
click.echo(key + ': ' + value)
242+
243+
244+
@click.command(help="Add custom request HEADER with given VALUE.")
245+
@click.argument('header', nargs=1)
246+
@click.argument('value', nargs=1)
247+
def headers_add(header, value):
248+
header = titlecase(header)
249+
headers = get_headers()
250+
headers[header] = value
251+
set_headers(headers)
252+
253+
click.echo(click.style('Added header', bold=True))
254+
click.echo('%s: %s' % (header, value))
255+
256+
257+
@click.command(help="Remove custom request HEADER.")
258+
@click.argument('header', nargs=1)
259+
def headers_remove(header):
260+
header = titlecase(header)
261+
headers = get_headers()
262+
headers.pop(header, None)
263+
set_headers(headers)
264+
265+
click.echo(click.style('Removed header', bold=True))
266+
click.echo(header)
267+
268+
172269
client.add_command(get)
173270
client.add_command(show)
174271
client.add_command(action)
175272
client.add_command(clear)
176273

274+
client.add_command(credentials)
275+
credentials.add_command(credentials_add, name='add')
276+
credentials.add_command(credentials_remove, name='remove')
277+
credentials.add_command(credentials_show, name='show')
278+
279+
client.add_command(headers)
280+
headers.add_command(headers_add, name='add')
281+
headers.add_command(headers_remove, name='remove')
282+
headers.add_command(headers_show, name='show')
283+
177284

178285
if __name__ == '__main__':
179286
client()

coreapi/transport.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,18 @@ def transition(self, link, params=None, session=None, link_ancestors=None):
1717
class HTTPTransport(BaseTransport):
1818
schemes = ['http', 'https']
1919

20-
def __init__(self, credentials=None):
20+
def __init__(self, credentials=None, headers=None):
2121
self._credentials = itypes.Dict(credentials or {})
22+
self._headers = itypes.Dict(headers or {})
2223

2324
@property
2425
def credentials(self):
2526
return self._credentials
2627

28+
@property
29+
def headers(self):
30+
return self._headers
31+
2732
def transition(self, link, params=None, session=None, link_ancestors=None):
2833
if session is None:
2934
from coreapi import get_default_session
@@ -77,6 +82,9 @@ def make_http_request(self, session, url, action=None, params=None):
7782
if host in self.credentials:
7883
opts['headers']['authorization'] = self.credentials[host]
7984

85+
if self.headers:
86+
opts['headers'].update(self.headers)
87+
8088
return requests.request(method, url, **opts)
8189

8290
def load_document(self, session, response):

tests/test_integration.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ def mockreturn(method, url, headers):
6868

6969

7070
def test_get_session():
71-
session = get_session(credentials={'example.org': 'abc'})
71+
session = get_session(
72+
credentials={'example.org': 'abc'},
73+
headers={'user-agent': 'foo'}
74+
)
7275
assert len(session.transports) == 1
7376
assert session.transports[0].credentials == {'example.org': 'abc'}
77+
assert session.transports[0].headers == {'user-agent': 'foo'}

tests/test_transport.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# coding: utf-8
2-
from coreapi import get_default_session, Link
2+
from coreapi import get_session, get_default_session, Link
33
from coreapi.exceptions import TransportError
44
from coreapi.transport import HTTPTransport
55
import pytest
@@ -97,8 +97,8 @@ def mockreturn(method, url, headers):
9797
monkeypatch.setattr(requests, 'request', mockreturn)
9898

9999
credentials = {'example.org': 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='}
100-
transport = HTTPTransport(credentials=credentials)
101-
session = get_default_session()
100+
session = get_session(credentials=credentials)
101+
transport = session.transports[0]
102102

103103
# Requests to example.org include credentials.
104104
response = transport.make_http_request(session, 'http://example.org/123')
@@ -107,3 +107,20 @@ def mockreturn(method, url, headers):
107107
# Requests to other.org do not include credentials.
108108
response = transport.make_http_request(session, 'http://other.org/123')
109109
assert response.content == ''
110+
111+
112+
# Test custom headers
113+
114+
def test_headers(monkeypatch):
115+
def mockreturn(method, url, headers):
116+
return MockResponse(headers.get('User-Agent', ''))
117+
118+
monkeypatch.setattr(requests, 'request', mockreturn)
119+
120+
headers = {'User-Agent': 'Example v1.0'}
121+
session = get_session(headers=headers)
122+
transport = session.transports[0]
123+
124+
# Requests include custom headers.
125+
response = transport.make_http_request(session, 'http://example.org/123')
126+
assert response.content == 'Example v1.0'

0 commit comments

Comments
 (0)