Show More
@@ -0,0 +1,32 b'' | |||||
|
1 | """Test the sessions service API.""" | |||
|
2 | ||||
|
3 | ||||
|
4 | import os | |||
|
5 | import sys | |||
|
6 | import json | |||
|
7 | import urllib | |||
|
8 | ||||
|
9 | import requests | |||
|
10 | ||||
|
11 | from IPython.html.tests.launchnotebook import NotebookTestBase | |||
|
12 | ||||
|
13 | ''' | |||
|
14 | class SessionAPITest(NotebookTestBase): | |||
|
15 | """Test the sessions web service API""" | |||
|
16 | ||||
|
17 | def base_url(self): | |||
|
18 | return super(SessionAPITest,self).base_url() + 'api/sessions' | |||
|
19 | ||||
|
20 | def test_no_sessions(self): | |||
|
21 | """Make sure there are no sessions running at the start""" | |||
|
22 | url = self.base_url() | |||
|
23 | r = requests.get(url) | |||
|
24 | self.assertEqual(r.json(), []) | |||
|
25 | ||||
|
26 | def test_start_session(self): | |||
|
27 | url = self.base_url() | |||
|
28 | param = urllib.urlencode({'notebook_path': 'test.ipynb'}) | |||
|
29 | r = requests.post(url, params=param) | |||
|
30 | print r | |||
|
31 | #self.assertNotEqual(r.json(), []) | |||
|
32 | ''' No newline at end of file |
1 | NO CONTENT: new file 100644 |
|
NO CONTENT: new file 100644 |
@@ -0,0 +1,124 b'' | |||||
|
1 | """Test the all of the services API.""" | |||
|
2 | ||||
|
3 | ||||
|
4 | import os | |||
|
5 | import sys | |||
|
6 | import json | |||
|
7 | import urllib | |||
|
8 | from zmq.utils import jsonapi | |||
|
9 | ||||
|
10 | import requests | |||
|
11 | ||||
|
12 | from IPython.html.tests.launchnotebook import NotebookTestBase | |||
|
13 | ||||
|
14 | class APITest(NotebookTestBase): | |||
|
15 | """Test the kernels web service API""" | |||
|
16 | ||||
|
17 | def base_url(self): | |||
|
18 | return super(APITest,self).base_url() | |||
|
19 | ||||
|
20 | def notebooks_url(self): | |||
|
21 | return self.base_url() + 'api/notebooks' | |||
|
22 | ||||
|
23 | def kernels_url(self): | |||
|
24 | return self.base_url() + 'api/kernels' | |||
|
25 | ||||
|
26 | def sessions_url(self): | |||
|
27 | return self.base_url() + 'api/sessions' | |||
|
28 | ||||
|
29 | def contents_url(self): | |||
|
30 | return self.contents_url() + 'api/contents' | |||
|
31 | ||||
|
32 | def mknb(self, name='', path='/'): | |||
|
33 | url = self.notebooks_url() + path | |||
|
34 | return url, requests.post(url) | |||
|
35 | ||||
|
36 | def delnb(self, name, path='/'): | |||
|
37 | url = self.notebooks_url() + path + name | |||
|
38 | r = requests.delete(url) | |||
|
39 | return r.status_code | |||
|
40 | ||||
|
41 | def test_no_notebooks(self): | |||
|
42 | url = self.notebooks_url() | |||
|
43 | r = requests.get(url) | |||
|
44 | self.assertEqual(r.json(), []) | |||
|
45 | ||||
|
46 | def test_root_notebook_handler(self): | |||
|
47 | # POST a notebook and test the dict thats returned. | |||
|
48 | url, nb = self.mknb() | |||
|
49 | data = nb.json() | |||
|
50 | assert isinstance(data, dict) | |||
|
51 | assert data.has_key("name") | |||
|
52 | assert data.has_key("path") | |||
|
53 | self.assertEqual(data['name'], u'Untitled0.ipynb') | |||
|
54 | self.assertEqual(data['path'], u'/') | |||
|
55 | ||||
|
56 | # GET list of notebooks in directory. | |||
|
57 | r = requests.get(url) | |||
|
58 | assert isinstance(r.json(), list) | |||
|
59 | assert isinstance(r.json()[0], dict) | |||
|
60 | ||||
|
61 | # GET with a notebook name. | |||
|
62 | url = self.notebooks_url() + '/Untitled0.ipynb' | |||
|
63 | r = requests.get(url) | |||
|
64 | assert isinstance(data, dict) | |||
|
65 | self.assertEqual(r.json(), data) | |||
|
66 | ||||
|
67 | # PATCH (rename) request. | |||
|
68 | new_name = {'name':'test.ipynb'} | |||
|
69 | r = requests.patch(url, data=jsonapi.dumps(new_name)) | |||
|
70 | data = r.json() | |||
|
71 | assert isinstance(data, dict) | |||
|
72 | ||||
|
73 | # make sure the patch worked. | |||
|
74 | new_url = self.notebooks_url() + '/test.ipynb' | |||
|
75 | r = requests.get(new_url) | |||
|
76 | assert isinstance(r.json(), dict) | |||
|
77 | self.assertEqual(r.json(), data) | |||
|
78 | ||||
|
79 | # GET bad (old) notebook name. | |||
|
80 | r = requests.get(url) | |||
|
81 | self.assertEqual(r.status_code, 404) | |||
|
82 | ||||
|
83 | # POST notebooks to folders one and two levels down. | |||
|
84 | os.makedirs(os.path.join(self.notebook_dir.name, 'foo')) | |||
|
85 | os.makedirs(os.path.join(self.notebook_dir.name, 'foo','bar')) | |||
|
86 | url, nb = self.mknb(path='/foo/') | |||
|
87 | url2, nb2 = self.mknb(path='/foo/bar/') | |||
|
88 | data = nb.json() | |||
|
89 | data2 = nb2.json() | |||
|
90 | assert isinstance(data, dict) | |||
|
91 | assert isinstance(data2, dict) | |||
|
92 | assert data.has_key("name") | |||
|
93 | assert data.has_key("path") | |||
|
94 | self.assertEqual(data['name'], u'Untitled0.ipynb') | |||
|
95 | self.assertEqual(data['path'], u'/foo/') | |||
|
96 | assert data2.has_key("name") | |||
|
97 | assert data2.has_key("path") | |||
|
98 | self.assertEqual(data2['name'], u'Untitled0.ipynb') | |||
|
99 | self.assertEqual(data2['path'], u'/foo/bar/') | |||
|
100 | ||||
|
101 | # GET request on notebooks one and two levels down. | |||
|
102 | r = requests.get(url+'Untitled0.ipynb') | |||
|
103 | r2 = requests.get(url2+'Untitled0.ipynb') | |||
|
104 | assert isinstance(r.json(), dict) | |||
|
105 | self.assertEqual(r.json(), data) | |||
|
106 | assert isinstance(r2.json(), dict) | |||
|
107 | self.assertEqual(r2.json(), data2) | |||
|
108 | ||||
|
109 | # PATCH notebooks that are one and two levels down. | |||
|
110 | new_name = {'name': 'testfoo.ipynb'} | |||
|
111 | r = requests.patch(url+'Untitled0.ipynb', data=jsonapi.dumps(new_name)) | |||
|
112 | r = requests.get(url+'testfoo.ipynb') | |||
|
113 | data = r.json() | |||
|
114 | assert isinstance(data, dict) | |||
|
115 | assert data.has_key('name') | |||
|
116 | self.assertEqual(data['name'], 'testfoo.ipynb') | |||
|
117 | r = requests.get(url+'Untitled0.ipynb') | |||
|
118 | self.assertEqual(r.status_code, 404) | |||
|
119 | ||||
|
120 | # DELETE notebooks | |||
|
121 | r = self.delnb('testfoo.ipynb', '/foo/') | |||
|
122 | r2 = self.delnb('Untitled0.ipynb', '/foo/bar/') | |||
|
123 | self.assertEqual(r, 204) | |||
|
124 | self.assertEqual(r2, 204) No newline at end of file |
@@ -109,7 +109,9 b' class TestContentManager(TestCase):' | |||||
109 | # and subdirectory. |
|
109 | # and subdirectory. | |
110 | contents = cm.list_contents('/') |
|
110 | contents = cm.list_contents('/') | |
111 | contents1 = cm.list_contents('fold1') |
|
111 | contents1 = cm.list_contents('fold1') | |
112 |
|
|
112 | assert isinstance(contents, list) | |
113 |
|
|
113 | assert isinstance(contents[0], dict) | |
|
114 | assert contents[0].has_key('path') | |||
|
115 | assert contents[0].has_key('path') | |||
114 | self.assertEqual(contents[0]['path'], '/') |
|
116 | self.assertEqual(contents[0]['path'], '/') | |
115 | self.assertEqual(contents1[0]['path'], 'fold1') |
|
117 | self.assertEqual(contents1[0]['path'], 'fold1') |
@@ -94,17 +94,16 b' class FileNotebookManager(NotebookManager):' | |||||
94 | def change_notebook(self, data, notebook_name, notebook_path='/'): |
|
94 | def change_notebook(self, data, notebook_name, notebook_path='/'): | |
95 | """Changes notebook""" |
|
95 | """Changes notebook""" | |
96 | changes = data.keys() |
|
96 | changes = data.keys() | |
97 | response = 200 |
|
|||
98 | for change in changes: |
|
97 | for change in changes: | |
99 | full_path = self.get_os_path(notebook_name, notebook_path) |
|
98 | full_path = self.get_os_path(notebook_name, notebook_path) | |
100 | if change == "name": |
|
99 | if change == "name": | |
101 | new_path = self.get_os_path(data['name'], notebook_path) |
|
100 | new_path = self.get_os_path(data['name'], notebook_path) | |
102 | if not os.path.isfile(new_path): |
|
101 | try: | |
103 | os.rename(full_path, |
|
102 | os.rename(full_path, | |
104 | self.get_os_path(data['name'], notebook_path)) |
|
103 | self.get_os_path(data['name'], notebook_path)) | |
105 | notebook_name = data['name'] |
|
104 | notebook_name = data['name'] | |
106 |
e |
|
105 | except OSError as e: | |
107 | response = 409 |
|
106 | raise web.HTTPError(409, u'Notebook name already exists.') | |
108 | if change == "path": |
|
107 | if change == "path": | |
109 | new_path = self.get_os_path(data['name'], data['path']) |
|
108 | new_path = self.get_os_path(data['name'], data['path']) | |
110 | stutil.move(full_path, new_path) |
|
109 | stutil.move(full_path, new_path) | |
@@ -112,7 +111,7 b' class FileNotebookManager(NotebookManager):' | |||||
112 | if change == "content": |
|
111 | if change == "content": | |
113 | self.save_notebook(data, notebook_name, notebook_path) |
|
112 | self.save_notebook(data, notebook_name, notebook_path) | |
114 | model = self.notebook_model(notebook_name, notebook_path) |
|
113 | model = self.notebook_model(notebook_name, notebook_path) | |
115 |
return model |
|
114 | return model | |
116 |
|
115 | |||
117 | def notebook_exists(self, name, path): |
|
116 | def notebook_exists(self, name, path): | |
118 | """Returns a True if the notebook exists. Else, returns False. |
|
117 | """Returns a True if the notebook exists. Else, returns False. | |
@@ -131,31 +130,6 b' class FileNotebookManager(NotebookManager):' | |||||
131 | path = self.get_os_path(name, path) |
|
130 | path = self.get_os_path(name, path) | |
132 | return os.path.isfile(path) |
|
131 | return os.path.isfile(path) | |
133 |
|
132 | |||
134 | def get_os_path(self, fname, path='/'): |
|
|||
135 | """Given a notebook name and a server URL path, return its file system |
|
|||
136 | path. |
|
|||
137 |
|
||||
138 | Parameters |
|
|||
139 | ---------- |
|
|||
140 | fname : string |
|
|||
141 | The name of a notebook file with the .ipynb extension |
|
|||
142 | path : string |
|
|||
143 | The relative URL path (with '/' as separator) to the named |
|
|||
144 | notebook. |
|
|||
145 |
|
||||
146 | Returns |
|
|||
147 | ------- |
|
|||
148 | path : string |
|
|||
149 | A file system path that combines notebook_dir (location where |
|
|||
150 | server started), the relative path, and the filename with the |
|
|||
151 | current operating system's url. |
|
|||
152 | """ |
|
|||
153 | parts = path.split('/') |
|
|||
154 | parts = [p for p in parts if p != ''] # remove duplicate splits |
|
|||
155 | parts += [fname] |
|
|||
156 | path = os.path.join(self.notebook_dir, *parts) |
|
|||
157 | return path |
|
|||
158 |
|
||||
159 | def read_notebook_object_from_path(self, path): |
|
133 | def read_notebook_object_from_path(self, path): | |
160 | """read a notebook object from a path""" |
|
134 | """read a notebook object from a path""" | |
161 | info = os.stat(path) |
|
135 | info = os.stat(path) |
@@ -36,7 +36,7 b' class NotebookRootHandler(IPythonHandler):' | |||||
36 | """get returns a list of notebooks from the location |
|
36 | """get returns a list of notebooks from the location | |
37 | where the server was started.""" |
|
37 | where the server was started.""" | |
38 | nbm = self.notebook_manager |
|
38 | nbm = self.notebook_manager | |
39 | notebooks = nbm.list_notebooks("") |
|
39 | notebooks = nbm.list_notebooks("/") | |
40 | self.finish(jsonapi.dumps(notebooks)) |
|
40 | self.finish(jsonapi.dumps(notebooks)) | |
41 |
|
41 | |||
42 | @web.authenticated |
|
42 | @web.authenticated | |
@@ -53,18 +53,9 b' class NotebookRootHandler(IPythonHandler):' | |||||
53 | fname = nbm.new_notebook(notebook_path='/') |
|
53 | fname = nbm.new_notebook(notebook_path='/') | |
54 | self.set_header('Location', nbm.notebook_dir + fname) |
|
54 | self.set_header('Location', nbm.notebook_dir + fname) | |
55 | model = nbm.notebook_model(fname) |
|
55 | model = nbm.notebook_model(fname) | |
56 |
self.set_header('Location', '{0}api/notebooks/{1}'.format(self.base_project_url, |
|
56 | self.set_header('Location', '{0}api/notebooks/{1}'.format(self.base_project_url, fname)) | |
57 | self.finish(jsonapi.dumps(model)) |
|
57 | self.finish(jsonapi.dumps(model)) | |
58 |
|
58 | |||
59 |
|
||||
60 | class NotebookRootRedirect(IPythonHandler): |
|
|||
61 |
|
||||
62 | @web.authenticated |
|
|||
63 | def get(self): |
|
|||
64 | """get redirects to not include trailing backslash""" |
|
|||
65 | self.redirect("/api/notebooks") |
|
|||
66 |
|
||||
67 |
|
||||
68 | class NotebookHandler(IPythonHandler): |
|
59 | class NotebookHandler(IPythonHandler): | |
69 |
|
60 | |||
70 | SUPPORTED_METHODS = ('GET', 'PUT', 'PATCH', 'POST','DELETE') |
|
61 | SUPPORTED_METHODS = ('GET', 'PUT', 'PATCH', 'POST','DELETE') | |
@@ -86,7 +77,7 b' class NotebookHandler(IPythonHandler):' | |||||
86 | # get and return notebook representation |
|
77 | # get and return notebook representation | |
87 | format = self.get_argument('format', default='json') |
|
78 | format = self.get_argument('format', default='json') | |
88 | download = self.get_argument('download', default='False') |
|
79 | download = self.get_argument('download', default='False') | |
89 | model = nbm.notebook_model(name,path) |
|
80 | model = nbm.notebook_model(name, path) | |
90 | last_mod, representation, name = nbm.get_notebook(name, path, format) |
|
81 | last_mod, representation, name = nbm.get_notebook(name, path, format) | |
91 | self.set_header('Last-Modified', last_mod) |
|
82 | self.set_header('Last-Modified', last_mod) | |
92 |
|
83 | |||
@@ -109,8 +100,7 b' class NotebookHandler(IPythonHandler):' | |||||
109 | nbm = self.notebook_manager |
|
100 | nbm = self.notebook_manager | |
110 | name, path = nbm.named_notebook_path(notebook_path) |
|
101 | name, path = nbm.named_notebook_path(notebook_path) | |
111 | data = jsonapi.loads(self.request.body) |
|
102 | data = jsonapi.loads(self.request.body) | |
112 |
model |
|
103 | model = nbm.change_notebook(data, name, path) | |
113 | self.set_status(response) |
|
|||
114 | self.finish(jsonapi.dumps(model)) |
|
104 | self.finish(jsonapi.dumps(model)) | |
115 |
|
105 | |||
116 | @web.authenticated |
|
106 | @web.authenticated | |
@@ -217,7 +207,7 b' default_handlers = [' | |||||
217 | ModifyNotebookCheckpointsHandler), |
|
207 | ModifyNotebookCheckpointsHandler), | |
218 | (r"api/notebooks/%s/" % _notebook_path_regex, NotebookHandler), |
|
208 | (r"api/notebooks/%s/" % _notebook_path_regex, NotebookHandler), | |
219 | (r"api/notebooks/%s" % _notebook_path_regex, NotebookHandler), |
|
209 | (r"api/notebooks/%s" % _notebook_path_regex, NotebookHandler), | |
220 |
(r"api/notebooks/", NotebookRoot |
|
210 | (r"api/notebooks/", NotebookRootHandler), | |
221 | (r"api/notebooks", NotebookRootHandler), |
|
211 | (r"api/notebooks", NotebookRootHandler), | |
222 | ] |
|
212 | ] | |
223 |
|
213 |
@@ -71,6 +71,32 b' class NotebookManager(LoggingConfigurable):' | |||||
71 | name = None |
|
71 | name = None | |
72 | path = "/".join(names) + '/' |
|
72 | path = "/".join(names) + '/' | |
73 | return name, path |
|
73 | return name, path | |
|
74 | ||||
|
75 | def get_os_path(self, fname=None, path='/'): | |||
|
76 | """Given a notebook name and a server URL path, return its file system | |||
|
77 | path. | |||
|
78 | ||||
|
79 | Parameters | |||
|
80 | ---------- | |||
|
81 | fname : string | |||
|
82 | The name of a notebook file with the .ipynb extension | |||
|
83 | path : string | |||
|
84 | The relative URL path (with '/' as separator) to the named | |||
|
85 | notebook. | |||
|
86 | ||||
|
87 | Returns | |||
|
88 | ------- | |||
|
89 | path : string | |||
|
90 | A file system path that combines notebook_dir (location where | |||
|
91 | server started), the relative path, and the filename with the | |||
|
92 | current operating system's url. | |||
|
93 | """ | |||
|
94 | parts = path.split('/') | |||
|
95 | parts = [p for p in parts if p != ''] # remove duplicate splits | |||
|
96 | if fname is not None: | |||
|
97 | parts += [fname] | |||
|
98 | path = os.path.join(self.notebook_dir, *parts) | |||
|
99 | return path | |||
74 |
|
100 | |||
75 | def url_encode(self, path): |
|
101 | def url_encode(self, path): | |
76 | """Returns the path with all special characters URL encoded""" |
|
102 | """Returns the path with all special characters URL encoded""" | |
@@ -135,7 +161,7 b' class NotebookManager(LoggingConfigurable):' | |||||
135 | model = {"name": notebook_name, |
|
161 | model = {"name": notebook_name, | |
136 | "path": notebook_path, |
|
162 | "path": notebook_path, | |
137 | "last_modified (UTC)": last_modified.ctime()} |
|
163 | "last_modified (UTC)": last_modified.ctime()} | |
138 |
if content |
|
164 | if content is True: | |
139 | model['content'] = contents |
|
165 | model['content'] = contents | |
140 | return model |
|
166 | return model | |
141 |
|
167 | |||
@@ -196,7 +222,7 b' class NotebookManager(LoggingConfigurable):' | |||||
196 | nb.metadata.name = name |
|
222 | nb.metadata.name = name | |
197 | self.write_notebook_object(nb, name, notebook_path, new_name) |
|
223 | self.write_notebook_object(nb, name, notebook_path, new_name) | |
198 |
|
224 | |||
199 |
def write_notebook_object(self, nb, notebook_name= |
|
225 | def write_notebook_object(self, nb, notebook_name='/', notebook_path='/', new_name=None): | |
200 | """Write a notebook object and return its notebook_name. |
|
226 | """Write a notebook object and return its notebook_name. | |
201 |
|
227 | |||
202 | If notebook_name is None, this method should create a new notebook_name. |
|
228 | If notebook_name is None, this method should create a new notebook_name. |
@@ -28,6 +28,7 b' class NotebookTestBase(TestCase):' | |||||
28 | '--ipython-dir=%s' % self.ipython_dir.name, |
|
28 | '--ipython-dir=%s' % self.ipython_dir.name, | |
29 | '--notebook-dir=%s' % self.notebook_dir.name |
|
29 | '--notebook-dir=%s' % self.notebook_dir.name | |
30 | ] |
|
30 | ] | |
|
31 | #self.notebook = Popen(notebook_args) | |||
31 | self.notebook = Popen(notebook_args, stdout=PIPE, stderr=PIPE) |
|
32 | self.notebook = Popen(notebook_args, stdout=PIPE, stderr=PIPE) | |
32 | time.sleep(3.0) |
|
33 | time.sleep(3.0) | |
33 |
|
34 |
General Comments 0
You need to be logged in to leave comments.
Login now