Merge ~jsseidel/maas:jsseidel-files-api-annotations into maas:master

Proposed by Spencer Seidel
Status: Merged
Approved by: Spencer Seidel
Approved revision: 9342732f6c2bda6b949d4c163ba13c88da61d97e
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~jsseidel/maas:jsseidel-files-api-annotations
Merge into: maas:master
Diff against target: 215 lines (+98/-29)
3 files modified
src/maasserver/api/examples/files.json (+5/-0)
src/maasserver/api/files.py (+91/-27)
src/maasserver/api/tests/test_doc.py (+2/-2)
Reviewer Review Type Date Requested Status
Mike Pontillo (community) Approve
MAAS Lander Approve
Review via email: mp+361020@code.launchpad.net

Commit message

Added API annotations and examples to files.

To post a comment you must log in.
Revision history for this message
Spencer Seidel (jsseidel) wrote :

Note that I had to alter the test because it used strings found in the old files.py docstrings.

Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b jsseidel-files-api-annotations lp:~jsseidel/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 9342732f6c2bda6b949d4c163ba13c88da61d97e

review: Approve
Revision history for this message
Mike Pontillo (mpontillo) wrote :

Looks good! Just a minor question below.

review: Approve
Revision history for this message
Spencer Seidel (jsseidel) :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/maasserver/api/examples/files.json b/src/maasserver/api/examples/files.json
2new file mode 100644
3index 0000000..6bb6f86
4--- /dev/null
5+++ b/src/maasserver/api/examples/files.json
6@@ -0,0 +1,5 @@
7+{
8+ "files-placeholder": {
9+ "message": "Information about this object is not available at this time."
10+ }
11+}
12diff --git a/src/maasserver/api/files.py b/src/maasserver/api/files.py
13index 292bc74..ab4c247 100644
14--- a/src/maasserver/api/files.py
15+++ b/src/maasserver/api/files.py
16@@ -34,11 +34,19 @@ from piston3.utils import rc
17
18
19 def get_file_by_name(handler, request):
20- """Get a named file from the file storage.
21+ """@description-title Get a named file
22+ @description Get a named file from the file storage.
23
24- :param filename: The exact name of the file you want to get.
25- :type filename: string
26- :return: The file is returned in the response content.
27+ @param (string) "filename" [required=true] The name of the file.
28+
29+ @success (http-status-code) "server-success" 200
30+ @success (json) "success-json" A JSON object containing the requested file.
31+ @success-example "success-json" [exkey=files-placeholder] placeholder text
32+
33+ @error (http-status-code) "404" 404
34+ @error (content) "not-found" The requested file is not found.
35+ @error-example "not-found"
36+ Not Found
37 """
38 filename = get_mandatory_param(request.GET, 'filename')
39 try:
40@@ -50,11 +58,19 @@ def get_file_by_name(handler, request):
41
42
43 def get_file_by_key(handler, request):
44- """Get a file from the file storage using its key.
45+ """@description-title Get a file by key
46+ @description Get a file from the file storage with the given key.
47+
48+ @param (string) "key" [required=true] The file's key.
49
50- :param key: The exact key of the file you want to get.
51- :type key: string
52- :return: The file is returned in the response content.
53+ @success (http-status-code) "server-success" 200
54+ @success (json) "success-json" A JSON object containing the requested file.
55+ @success-example "success-json" [exkey=files-placeholder] placeholder text
56+
57+ @error (http-status-code) "404" 404
58+ @error (content) "not-found" The requested file is not found.
59+ @error-example "not-found"
60+ Not Found
61 """
62 key = get_mandatory_param(request.GET, 'key')
63 db_file = get_object_or_404(FileStorage, key=key)
64@@ -110,7 +126,8 @@ def json_file_storage(stored_file, request):
65
66
67 class FileHandler(OperationsHandler):
68- """Manage a FileStorage object.
69+ """
70+ Manage a FileStorage object.
71
72 The file is identified by its filename and owner.
73 """
74@@ -120,9 +137,24 @@ class FileHandler(OperationsHandler):
75 create = update = None
76
77 def read(self, request, filename):
78- """GET a FileStorage object as a json object.
79+ """@description-title Read a stored file
80+ @description Reads a stored file with the given file name.
81
82- The 'content' of the file is base64-encoded."""
83+ The content of the file is base64-encoded.
84+
85+ @param (string) "{filename}" [required=true] The name of the file.
86+
87+ @success (http-status-code) "server-success" 200
88+ @success (json) "success-json" A JSON object containing the requested
89+ file.
90+ @success-example "success-json" [exkey=files-placeholder] placeholder
91+ text
92+
93+ @error (http-status-code) "404" 404
94+ @error (content) "not-found" The requested file is not found.
95+ @error-example "not-found"
96+ Not Found
97+ """
98 try:
99 stored_file = get_object_or_404(
100 FileStorage, filename=filename, owner=request.user)
101@@ -140,7 +172,18 @@ class FileHandler(OperationsHandler):
102 status=int(http.client.OK))
103
104 def delete(self, request, filename):
105- """Delete a FileStorage object."""
106+ """@description-title Delete a file
107+ @description Delete a file with the given file name.
108+
109+ @param (string) "{filename}" [required=true] The name of the file.
110+
111+ @success (http-status-code) "server-success" 204
112+
113+ @error (http-status-code) "404" 404
114+ @error (content) "not-found" The requested file is not found.
115+ @error-example "not-found"
116+ Not Found
117+ """
118 stored_file = get_object_or_404(
119 FileStorage, filename=filename, owner=request.user)
120 stored_file.delete()
121@@ -166,17 +209,23 @@ class FilesHandler(OperationsHandler):
122 idempotent=True, exported_as='get_by_key')(get_file_by_key)
123
124 def create(self, request):
125- """Add a new file to the file storage.
126+ """@description-title Add a new file
127+ @description Add a new file to the file storage.
128+
129+ @param (string) "filename" [required=true] The file name to use in
130+ storage.
131
132- :param filename: The file name to use in the storage.
133- :type filename: string
134- :param file: Actual file data with content type
135- application/octet-stream
136+ @param (string) "file" [required=true] File data. Content type must be
137+ ``application/octet-stream``.
138
139- Returns 400 if any of these conditions apply:
140- - The filename is missing from the parameters
141- - The file data is missing
142- - More than one file is supplied
143+ @success (http-status-code) "server-success" 200
144+ @success (json) "success-json" A JSON object containing the new file.
145+ @success-example "success-json" [exkey=files-placeholder] placeholder
146+ text
147+
148+ @error (http-status-code) "400" 400
149+ @error (content) "arg-prob" The filename is missing, the file data is
150+ missing or more than one file is supplied.
151 """
152 filename = request.data.get("filename", None)
153 if not filename:
154@@ -195,13 +244,20 @@ class FilesHandler(OperationsHandler):
155 return HttpResponse('', status=int(http.client.CREATED))
156
157 def read(self, request):
158- """List the files from the file storage.
159+ """@description-title List files
160+ @description List the files from the file storage.
161
162 The returned files are ordered by file name and the content is
163 excluded.
164
165- :param prefix: Optional prefix used to filter out the returned files.
166- :type prefix: string
167+ @param (string) "prefix" [required=false] Prefix used to filter
168+ returned files.
169+
170+ @success (http-status-code) "server-success" 200
171+ @success (json) "success-json" A JSON object containing a list of the
172+ reqeusted file names.
173+ @success-example "success-json" [exkey=files-placeholder] placeholder
174+ text
175 """
176 prefix = request.GET.get("prefix", None)
177 files = FileStorage.objects.filter(owner=request.user)
178@@ -211,10 +267,18 @@ class FilesHandler(OperationsHandler):
179 return files
180
181 def delete(self, request, **kwargs):
182- """Delete a FileStorage object.
183+ """@description-title Delete a file
184+ @description Delete a stored file.
185+
186+ @param (string) "filename" [required=true] The filename of the object
187+ to be deleted.
188+
189+ @success (http-status-code) "server-success" 204
190
191- :param filename: The filename of the object to be deleted.
192- :type filename: unicode
193+ @error (http-status-code) "404" 404
194+ @error (content) "not-found" The requested file is not found.
195+ @error-example "not-found"
196+ Not Found
197 """
198 # It is valid for a path in a POST, PUT, or DELETE (or any other HTTP
199 # method) to contain a query string. However, Django only makes
200diff --git a/src/maasserver/api/tests/test_doc.py b/src/maasserver/api/tests/test_doc.py
201index 0c78828..51d77b4 100644
202--- a/src/maasserver/api/tests/test_doc.py
203+++ b/src/maasserver/api/tests/test_doc.py
204@@ -225,9 +225,9 @@ class TestHandlers(MAASTestCase):
205 # Doc for a method.
206 "Manage custom commissioning scripts.",
207 # Doc for a method parameter.
208- "GET a FileStorage object as a json object.",
209+ "Reads a stored file",
210 # Doc for a method parameter (:param: doc).
211- "Optional prefix used to filter out the returned files.",
212+ "Prefix used to filter returned files.",
213 # Doc for a rendered docstring containing annotations
214 "\"resource_uri\": \"/MAAS/api",
215 # Doc for a rendered docstring containing annotations

Subscribers

People subscribed via source and target branches