Pulp bindings

Pulp currently provides python, ruby, typescript client bindings.

We depended on:

Pulp & OpenAPI Generator

The bindings are generated from the JSON OpenAPI schema on this endpoint:
/pulp/api/v3/docs/api.json?bindings&plugin=galaxy_ng
with the following script: https://github.com/pulp/pulp-openapi-generator/blob/master/generate.sh

The following OpenAPI schema:

"/api/galaxy/content/{path}/v3/artifacts/collections/": { "post": { "operationId": "create", "description": "Create an artifact and trigger an asynchronous task to create Collection content from it.", "summary": "Upload a collection", "parameters": [ { "in": "path", "name": "path", "schema": { "type": "string" }, "required": true } ], "tags": [ "Pulp_Ansible: Artifacts Collections V3" ], "requestBody": { "content": { "multipart/form-data": { "schema": { "$ref": "#/components/schemas/CollectionUploadWithDownloadUrl" } }, "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/CollectionUploadWithDownloadUrl" } } }, "required": true }, "security": [ { "cookieAuth": [] }, { "tokenAuth": [] }, { "basicAuth": [] } ], "responses": { "202": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AsyncOperationResponse" } } }, "description": "" } } } }

when converted to python bindings, would become:

class PulpAnsibleArtifactsCollectionsV3Api(object): """NOTE: This class is auto generated by OpenAPI Generator Ref: https://openapi-generator.tech Do not edit the class manually. """ def __init__(self, api_client=None): if api_client is None: api_client = ApiClient() self.api_client = api_client def create(self, path, file, **kwargs): # noqa: E501 """Upload a collection # noqa: E501 Create an artifact and trigger an asynchronous task to create Collection content from it. # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async_req=True >>> thread = api.create(path, file, async_req=True) >>> result = thread.get() :param async_req bool: execute request asynchronously :param str path: (required) :param file file: (required) :param str sha256: :param _preload_content: if False, the urllib3.HTTPResponse object will be returned without reading/decoding response data. Default is True. :param _request_timeout: timeout setting for this request. If one number provided, it will be total request timeout. It can also be a pair (tuple) of (connection, read) timeouts. :return: AsyncOperationResponse If the method is called asynchronously, returns the request thread. """ kwargs['_return_http_data_only'] = True return self.create_with_http_info(path, file, **kwargs) # noqa: E501 def create_with_http_info(self, path, file, **kwargs): # noqa: E501 """Upload a collection # noqa: E501 Create an artifact and trigger an asynchronous task to create Collection content from it. # noqa: E501 This method makes a synchronous HTTP request by default. To make an asynchronous HTTP request, please pass async_req=True >>> thread = api.create_with_http_info(path, file, async_req=True) >>> result = thread.get() :param async_req bool: execute request asynchronously :param str path: (required) :param file file: (required) :param str sha256: :param _return_http_data_only: response data without head status code and headers :param _preload_content: if False, the urllib3.HTTPResponse object will be returned without reading/decoding response data. Default is True. :param _request_timeout: timeout setting for this request. If one number provided, it will be total request timeout. It can also be a pair (tuple) of (connection, read) timeouts. :return: tuple(AsyncOperationResponse, status_code(int), headers(HTTPHeaderDict)) If the method is called asynchronously, returns the request thread. """ local_var_params = locals() all_params = [ 'path', 'file', 'sha256' ] all_params.extend( [ 'async_req', '_return_http_data_only', '_preload_content', '_request_timeout' ] ) for key, val in six.iteritems(local_var_params['kwargs']): if key not in all_params: raise ApiTypeError( "Got an unexpected keyword argument '%s'" " to method create" % key ) local_var_params[key] = val del local_var_params['kwargs'] # verify the required parameter 'path' is set if self.api_client.client_side_validation and ('path' not in local_var_params or # noqa: E501 local_var_params['path'] is None): # noqa: E501 raise ApiValueError("Missing the required parameter `path` when calling `create`") # noqa: E501 # verify the required parameter 'file' is set if self.api_client.client_side_validation and ('file' not in local_var_params or # noqa: E501 local_var_params['file'] is None): # noqa: E501 raise ApiValueError("Missing the required parameter `file` when calling `create`") # noqa: E501 collection_formats = {} path_params = {} if 'path' in local_var_params: path_params['path'] = local_var_params['path'] # noqa: E501 query_params = [] header_params = {} form_params = [] local_var_files = {} if 'file' in local_var_params: local_var_files['file'] = local_var_params['file'] # noqa: E501 if 'sha256' in local_var_params: form_params.append(('sha256', local_var_params['sha256'])) # noqa: E501 body_params = None # HTTP header `Accept` header_params['Accept'] = self.api_client.select_header_accept( ['application/json']) # noqa: E501 # HTTP header `Content-Type` header_params['Content-Type'] = self.api_client.select_header_content_type( # noqa: E501 ['multipart/form-data', 'application/x-www-form-urlencoded']) # noqa: E501 # Authentication setting auth_settings = ['basicAuth', 'cookieAuth', 'tokenAuth'] # noqa: E501 return self.api_client.call_api( '/api/galaxy/content/{path}/v3/artifacts/collections/', 'POST', path_params, query_params, header_params, body=body_params, post_params=form_params, files=local_var_files, response_type='AsyncOperationResponse', # noqa: E501 auth_settings=auth_settings, async_req=local_var_params.get('async_req'), _return_http_data_only=local_var_params.get('_return_http_data_only'), # noqa: E501 _preload_content=local_var_params.get('_preload_content', True), _request_timeout=local_var_params.get('_request_timeout'), collection_formats=collection_formats)
HTTP methods to Bindings methods:

get -> read
post -> create
put -> update
patch -> partial_update
delete -> delete

OpenAPI tag into Bindings class:

"Pulp_Ansible: Artifacts Collections V3" -> PulpAnsibleArtifactsCollectionsV3Api

Note: Generally the OpenAPI tag is generated by: URL path (/my/path/to/content -> My Path to Content), but it can be the endpoint pieces or the view name: https://github.com/pulp/pulpcore/blob/master/pulpcore/openapi/init.py#L40-L67

Note 2: You can specify the OpenAPI tag name at your view: https://docs.pulpproject.org/pulpcore/plugins/reference/how-to-doc-api.html#openapi-tags

Pulp & DRF Spectacular

We are using the following drf-spectacular settings: https://github.com/pulp/pulpcore/blob/master/pulpcore/app/settings.py#L264-L285
And we "patch" drf-spectacular here: https://github.com/pulp/pulpcore/blob/master/pulpcore/openapi/init.py

drf-spectacular inspects every view and its serializer and model to provide a proper OpenAPI schema.