Show More
@@ -0,0 +1,32 | |||
|
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 |
@@ -0,0 +1,124 | |||
|
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 class TestContentManager(TestCase): | |||
|
109 | 109 | # and subdirectory. |
|
110 | 110 | contents = cm.list_contents('/') |
|
111 | 111 | contents1 = cm.list_contents('fold1') |
|
112 |
|
|
|
113 |
|
|
|
112 | assert isinstance(contents, list) | |
|
113 | assert isinstance(contents[0], dict) | |
|
114 | assert contents[0].has_key('path') | |
|
115 | assert contents[0].has_key('path') | |
|
114 | 116 | self.assertEqual(contents[0]['path'], '/') |
|
115 | 117 | self.assertEqual(contents1[0]['path'], 'fold1') |
@@ -94,17 +94,16 class FileNotebookManager(NotebookManager): | |||
|
94 | 94 | def change_notebook(self, data, notebook_name, notebook_path='/'): |
|
95 | 95 | """Changes notebook""" |
|
96 | 96 | changes = data.keys() |
|
97 | response = 200 | |
|
98 | 97 | for change in changes: |
|
99 | 98 | full_path = self.get_os_path(notebook_name, notebook_path) |
|
100 | 99 | if change == "name": |
|
101 | 100 | new_path = self.get_os_path(data['name'], notebook_path) |
|
102 | if not os.path.isfile(new_path): | |
|
101 | try: | |
|
103 | 102 | os.rename(full_path, |
|
104 | 103 | self.get_os_path(data['name'], notebook_path)) |
|
105 | 104 | notebook_name = data['name'] |
|
106 |
e |
|
|
107 | response = 409 | |
|
105 | except OSError as e: | |
|
106 | raise web.HTTPError(409, u'Notebook name already exists.') | |
|
108 | 107 | if change == "path": |
|
109 | 108 | new_path = self.get_os_path(data['name'], data['path']) |
|
110 | 109 | stutil.move(full_path, new_path) |
@@ -112,7 +111,7 class FileNotebookManager(NotebookManager): | |||
|
112 | 111 | if change == "content": |
|
113 | 112 | self.save_notebook(data, notebook_name, notebook_path) |
|
114 | 113 | model = self.notebook_model(notebook_name, notebook_path) |
|
115 |
return model |
|
|
114 | return model | |
|
116 | 115 | |
|
117 | 116 | def notebook_exists(self, name, path): |
|
118 | 117 | """Returns a True if the notebook exists. Else, returns False. |
@@ -131,31 +130,6 class FileNotebookManager(NotebookManager): | |||
|
131 | 130 | path = self.get_os_path(name, path) |
|
132 | 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 | 133 | def read_notebook_object_from_path(self, path): |
|
160 | 134 | """read a notebook object from a path""" |
|
161 | 135 | info = os.stat(path) |
@@ -36,7 +36,7 class NotebookRootHandler(IPythonHandler): | |||
|
36 | 36 | """get returns a list of notebooks from the location |
|
37 | 37 | where the server was started.""" |
|
38 | 38 | nbm = self.notebook_manager |
|
39 | notebooks = nbm.list_notebooks("") | |
|
39 | notebooks = nbm.list_notebooks("/") | |
|
40 | 40 | self.finish(jsonapi.dumps(notebooks)) |
|
41 | 41 | |
|
42 | 42 | @web.authenticated |
@@ -53,18 +53,9 class NotebookRootHandler(IPythonHandler): | |||
|
53 | 53 | fname = nbm.new_notebook(notebook_path='/') |
|
54 | 54 | self.set_header('Location', nbm.notebook_dir + fname) |
|
55 | 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 | 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 | 59 | class NotebookHandler(IPythonHandler): |
|
69 | 60 | |
|
70 | 61 | SUPPORTED_METHODS = ('GET', 'PUT', 'PATCH', 'POST','DELETE') |
@@ -86,7 +77,7 class NotebookHandler(IPythonHandler): | |||
|
86 | 77 | # get and return notebook representation |
|
87 | 78 | format = self.get_argument('format', default='json') |
|
88 | 79 | download = self.get_argument('download', default='False') |
|
89 | model = nbm.notebook_model(name,path) | |
|
80 | model = nbm.notebook_model(name, path) | |
|
90 | 81 | last_mod, representation, name = nbm.get_notebook(name, path, format) |
|
91 | 82 | self.set_header('Last-Modified', last_mod) |
|
92 | 83 | |
@@ -109,8 +100,7 class NotebookHandler(IPythonHandler): | |||
|
109 | 100 | nbm = self.notebook_manager |
|
110 | 101 | name, path = nbm.named_notebook_path(notebook_path) |
|
111 | 102 | data = jsonapi.loads(self.request.body) |
|
112 |
model |
|
|
113 | self.set_status(response) | |
|
103 | model = nbm.change_notebook(data, name, path) | |
|
114 | 104 | self.finish(jsonapi.dumps(model)) |
|
115 | 105 | |
|
116 | 106 | @web.authenticated |
@@ -217,7 +207,7 default_handlers = [ | |||
|
217 | 207 | ModifyNotebookCheckpointsHandler), |
|
218 | 208 | (r"api/notebooks/%s/" % _notebook_path_regex, NotebookHandler), |
|
219 | 209 | (r"api/notebooks/%s" % _notebook_path_regex, NotebookHandler), |
|
220 |
(r"api/notebooks/", NotebookRoot |
|
|
210 | (r"api/notebooks/", NotebookRootHandler), | |
|
221 | 211 | (r"api/notebooks", NotebookRootHandler), |
|
222 | 212 | ] |
|
223 | 213 |
@@ -71,6 +71,32 class NotebookManager(LoggingConfigurable): | |||
|
71 | 71 | name = None |
|
72 | 72 | path = "/".join(names) + '/' |
|
73 | 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 | 101 | def url_encode(self, path): |
|
76 | 102 | """Returns the path with all special characters URL encoded""" |
@@ -135,7 +161,7 class NotebookManager(LoggingConfigurable): | |||
|
135 | 161 | model = {"name": notebook_name, |
|
136 | 162 | "path": notebook_path, |
|
137 | 163 | "last_modified (UTC)": last_modified.ctime()} |
|
138 |
if content |
|
|
164 | if content is True: | |
|
139 | 165 | model['content'] = contents |
|
140 | 166 | return model |
|
141 | 167 | |
@@ -196,7 +222,7 class NotebookManager(LoggingConfigurable): | |||
|
196 | 222 | nb.metadata.name = name |
|
197 | 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 | 226 | """Write a notebook object and return its notebook_name. |
|
201 | 227 | |
|
202 | 228 | If notebook_name is None, this method should create a new notebook_name. |
General Comments 0
You need to be logged in to leave comments.
Login now