@@ -857,10 +857,10 @@ def parameter_hoisting_note(method: str,
857857def repository_search_spec (* , post : bool ):
858858 id_spec_link = '#operations-Index-get_index__entity_type___entity_id_'
859859 return {
860- 'summary' : fd ( f'''
861- Search an index for entities of interest
862- { ", with filters provided in the request body" if post else "" } .
863- ''' ),
860+ 'summary' : (
861+ ' Search an index for entities of interest' +
862+ iif ( post , ', with large parameters provided in the request body' )
863+ ),
864864 'deprecated' : post ,
865865 'description' :
866866 iif (post , parameter_hoisting_note ('GET' , '/index/files' , 'POST' ) + fd ('''
@@ -1096,14 +1096,34 @@ def get_summary():
10961096 authentication = request .authentication )
10971097
10981098
1099- def manifest_route (* , fetch : bool , initiate : bool ):
1099+ post_manifest_example_url = (
1100+ f'{ app .base_url } /manifest/files'
1101+ f'?catalog={ list (config .catalogs .keys ())[0 ]} '
1102+ '&filters={…}'
1103+ f'&format={ app .metadata_plugin .manifest_formats [0 ].value } '
1104+ )
1105+
1106+
1107+ def manifest_route (* , fetch : bool , initiate : bool , curl : bool = False ):
1108+ if initiate :
1109+ if curl :
1110+ assert not fetch
1111+ method = 'POST'
1112+ else :
1113+ method = 'PUT'
1114+ else :
1115+ assert not curl
1116+ method = 'GET'
11001117 return app .route (
11011118 # The path parameter could be a token *or* an object key, but we don't
11021119 # want to complicate the API with this detail
11031120 ('/fetch' if fetch else '' )
11041121 + ('/manifest/files' if initiate else '/manifest/files/{token}' ),
11051122 # The initial PUT request is idempotent.
1106- methods = ['PUT' if initiate else 'GET' ],
1123+ methods = [method ],
1124+ # In order to support requests made with `curl` and its `--data` option,
1125+ # we accept the `application/x-www-form-urlencoded` content-type.
1126+ content_types = ['application/json' , 'application/x-www-form-urlencoded' ],
11071127 interactive = fetch ,
11081128 cors = True ,
11091129 path_spec = None if initiate else {
@@ -1115,26 +1135,58 @@ def manifest_route(*, fetch: bool, initiate: bool):
11151135 },
11161136 spec = {
11171137 'tags' : ['Manifests' ],
1138+ 'deprecated' : curl ,
11181139 'summary' :
11191140 (
11201141 'Initiate the preparation of a manifest'
11211142 if initiate else
11221143 'Determine status of a manifest preparation job'
11231144 ) + (
11241145 ' via XHR' if fetch else ''
1146+ ) + (
1147+ ' as an alternative to PUT for curl users' if curl else ''
11251148 ),
1126- 'description' : fd ('''
1127- Create a manifest preparation job, returning either
1128-
1129- - a 301 redirect to the URL of the status of that job or
1149+ 'description' : (
1150+ fd ('''
1151+ Create a manifest preparation job, returning either
11301152
1131- - a 302 redirect to the URL of an already prepared manifest.
1153+ - a 301 redirect to the URL of the status of that job or
11321154
1133- This endpoint is not suitable for interactive use via the
1134- Swagger UI. Please use [PUT /fetch/manifest/files][1] instead.
1155+ - a 302 redirect to the URL of an already prepared manifest.
1156+ ''' )
1157+ + iif (not curl , fd (f'''
1158+ This endpoint is not suitable for interactive use via the
1159+ Swagger UI. Please use [{ method } /fetch/manifest/files][1]
1160+ instead.
11351161
1136- [1]: #operations-Manifests-put_fetch_manifest_files
1137- ''' ) + parameter_hoisting_note ('PUT' , '/manifest/files' , 'PUT' )
1162+ [1]: #operations-Manifests-{ method .lower ()} _fetch_manifest_files
1163+ ''' ))
1164+ + parameter_hoisting_note (method , '/manifest/files' , method )
1165+ + iif (curl , fd (f'''
1166+ Requests to this endpoint are idempotent, so PUT would be
1167+ the more standards-compliant method to use. POST is offered
1168+ as a convenience for `curl` users, exploiting the fact that
1169+ `curl` drops to GET when following a redirect in response to
1170+ a POST, but not a PUT request. This is the only reason for
1171+ the deprecation of this endpoint and there are currently no
1172+ plans to remove it.
1173+
1174+ To use this endpoint with `curl`, pass the `--location` and
1175+ `--data` options. This makes `curl` automatically follow the
1176+ intermediate redirects to the GET /manifest/files endpoint,
1177+ and ultimately to the URL that yields the manifest. Example:
1178+
1179+ ```
1180+ curl --data "" --location { post_manifest_example_url }
1181+ ```
1182+
1183+ In order to facilitate this, a POST request to this endpoint
1184+ may have a `Content-Type` header of
1185+ `application/x-www-form-urlencoded`, which is what the
1186+ `--data` option sends. The body must be empty in that case
1187+ and parameters cannot be hoisted as described above.
1188+ ''' ))
1189+ )
11381190 if initiate and not fetch else
11391191 fd ('''
11401192 Check on the status of an ongoing manifest preparation job,
@@ -1150,15 +1202,17 @@ def manifest_route(*, fetch: bool, initiate: bool):
11501202 instead.
11511203
11521204 [1]: #operations-Manifests-get_fetch_manifest_files__token_
1153- ''' ) if not initiate and not fetch else fd ('''
1205+ ''' )
1206+ if not initiate and not fetch else
1207+ fd (f'''
11541208 Create a manifest preparation job, returning a 200 status
11551209 response whose JSON body emulates the HTTP headers that would be
1156- found in a response to an equivalent request to the [PUT
1210+ found in a response to an equivalent request to the [{ method }
11571211 /manifest/files][1] endpoint.
11581212
11591213 Whenever client-side JavaScript code is used in a web
11601214 application to request the preparation of a manifest from Azul,
1161- this endpoint should be used instead of [PUT
1215+ this endpoint should be used instead of [{ method }
11621216 /manifest/files][1]. This way, the client can use XHR to make
11631217 the request, retaining full control over the handling of
11641218 redirects and enabling the client to bypass certain limitations
@@ -1168,8 +1222,9 @@ def manifest_route(*, fetch: bool, initiate: bool):
11681222 upper limit on the number of consecutive redirects, before the
11691223 manifest generation job is done.
11701224
1171- [1]: #operations-Manifests-put_manifest_files
1172- ''' ) + parameter_hoisting_note ('PUT' , '/fetch/manifest/files' , 'PUT' )
1225+ [1]: #operations-Manifests-{ method .lower ()} _manifest_files
1226+ ''' )
1227+ + parameter_hoisting_note (method , '/fetch/manifest/files' , method )
11731228 if initiate and fetch else
11741229 fd ('''
11751230 Check on the status of an ongoing manifest preparation job,
@@ -1314,10 +1369,10 @@ def manifest_route(*, fetch: bool, initiate: bool):
13141369
13151370 For a detailed description of these properties see the
13161371 documentation for the respective response headers
1317- documented under ''' ) + (fd ('''
1318- [PUT /manifest/files][1].
1372+ documented under ''' ) + (fd (f '''
1373+ [{ method } /manifest/files][1].
13191374
1320- [1]: #operations-Manifests-put_manifest_files
1375+ [1]: #operations-Manifests-{ method . lower () } _manifest_files
13211376 ''' ) if initiate else fd ('''
13221377 [GET /manifest/files/{token}][1].
13231378
@@ -1359,6 +1414,7 @@ def manifest_route(*, fetch: bool, initiate: bool):
13591414 )
13601415
13611416
1417+ @manifest_route (fetch = False , initiate = True , curl = True )
13621418@manifest_route (fetch = False , initiate = True )
13631419def file_manifest ():
13641420 return _file_manifest (fetch = False )
@@ -1381,6 +1437,14 @@ def fetch_file_manifest_with_token(token: str):
13811437
13821438def _file_manifest (fetch : bool , token_or_key : str | None = None ):
13831439 request = app .current_request
1440+ post = request .method == 'POST'
1441+ if (
1442+ post
1443+ and request .headers .get ('content-type' ) == 'application/x-www-form-urlencoded'
1444+ and request .raw_body != b''
1445+ ):
1446+ raise BRE ('The body must be empty for a POST request of content-type '
1447+ '`application/x-www-form-urlencoded` to this endpoint' )
13841448 query_params = request .query_params or {}
13851449 _hoist_parameters (query_params , request )
13861450 if token_or_key is None :
0 commit comments