API design guidelines.

This commit is contained in:
John Chilton
2021-01-13 18:18:39 -05:00
parent 58620c0602
commit 21f06bafef
5 changed files with 176 additions and 256 deletions
-70
View File
@@ -1,70 +0,0 @@
API Design Guidelines
=====================
The following section outlines guidelines related to extending and/or modifying
the Galaxy API. The Galaxy API has grown in an ad-hoc fashion over time by
many contributors and so clients SHOULD NOT expect the API will conform to
these guidelines - but developers contributing to the Galaxy API SHOULD follow
these guidelines.
- API functionality should include docstring documentation for consumption
at docs.galaxyproject.org.
- Developers should familiarize themselves with the HTTP status code definitions
http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html. The API responses
should properly set the status code according to the result - in particular
2XX responses should be used for successful requests, 4XX for various
kinds of client errors, and 5XX for the errors on the server side.
- If there is an error processing some part of the request (one item in a list
for instance), the status code should be set to reflect the error and the
partial result may or may not be returned depending on the controller -
this behavior should be documented.
- API methods should throw a finite number of exceptions
(defined in :mod:`galaxy.exceptions`) and these should subclass
`MessageException` and not paste/wsgi HTTP exceptions. When possible,
the framework itself should be responsible for catching these exceptions,
setting the status code, and building an error response.
- Error responses should not consist of plain text strings - they should be
dictionaries describing the error and containing the following::
{
"status_code": 400,
"err_code": 400007,
"err_msg": "Request contained invalid parameter, action could not be completed.",
"type": "error",
"extra_error_info": "Extra information."
}
Various error conditions (once a format has been chosen and framework to
enforce it in place) should be spelled out in this document.
- Backward compatibility is important and should be maintained when possible.
If changing behavior in a non-backward compatible way please ensure one
of the following holds - there is a strong reason to believe no consumers
depend on a behavior, the behavior is effectively broken, or the API
method being modified has not been part of a tagged dist release.
The following bullet points represent good practices more than guidelines, please
consider them when modifying the API.
- Functionality should not be copied and pasted between controllers -
consider refactoring functionality into associated classes or short of
that into Mixins (http://en.wikipedia.org/wiki/Composition_over_inheritance)
or into Managers (:mod:`galaxy.managers`).
- API additions are more permanent changes to Galaxy than many other potential
changes and so a second opinion on API changes should be sought.
- New API functionality should include functional tests. These functional
tests should be implemented in Python and placed in
`lib/galaxy_test/api`.
- Changes to reflect modifications to the API should be pushed upstream to
the BioBlend project if possible.
Longer term goals/notes.
- It would be advantageous to have a clearer separation of anonymous and
admin handling functionality.
- If at some point in the future, functionality needs to be added that
breaks backward compatibility in a significant way to a component used by
the community - a "dev" variant of the API will be established and
the community should be alerted and given a timeframe for when the old
behavior will be replaced with the new behavior.
- Consistent standards for range-based requests, batch requests, filtered
requests, etc... should be established and documented here.
+3 -183
View File
@@ -1,186 +1,6 @@
Quickstart
==========
Log in as your user, navigate to the API Keys page in the User menu, and
generate a new API key. Make a note of the API key, and then pull up a
terminal. Now we'll use the display.py script in your ``galaxy/scripts/api``
directory for a short example, which assumes your local galaxy server is
running on the default port 8080::
% ./display.py my_key http://localhost:8080/api/histories
Collection Members
------------------
#1: /api/histories/8c49be448cfe29bc
name: Unnamed history
id: 8c49be448cfe29bc
#2: /api/histories/33b43b4e7093c91f
name: output test
id: 33b43b4e7093c91f
The result is a Collection of the histories of the user specified by the API
key (you). To look at the details of a particular history, say #1 above, do
the following::
% ./display.py my_key http://localhost:8080/api/histories/8c49be448cfe29bc
Member Information
------------------
state_details: {'ok': 1, 'failed_metadata': 0, 'upload': 0, 'discarded': 0, 'running': 0, 'setting_metadata': 0, 'error': 0, 'new': 0, 'queued': 0, 'empty': 0}
state: ok
contents_url: /api/histories/8c49be448cfe29bc/contents
id: 8c49be448cfe29bc
name: Unnamed history
This gives detailed information about the specific member in question, in this
case the History. To view history contents, do the following::
% ./display.py my_key http://localhost:8080/api/histories/8c49be448cfe29bc/contents
Collection Members
------------------
#1: /api/histories/8c49be448cfe29bc/contents/6f91353f3eb0fa4a
name: Pasted Entry
type: file
id: 6f91353f3eb0fa4a
What we have here is another Collection of items containing all of the datasets
in this particular history. Finally, to view details of a particular dataset
in this collection, execute the following::
% ./display.py my_key http://localhost:8080/api/histories/8c49be448cfe29bc/contents/6f91353f3eb0fa4a
Member Information
------------------
misc_blurb: 1 line
name: Pasted Entry
data_type: txt
deleted: False
file_name: /Users/yoplait/work/galaxy-stock/database/files/000/dataset_82.dat
state: ok
download_url: /datasets/6f91353f3eb0fa4a/display?to_ext=txt
visible: True
genome_build: ?
model_class: HistoryDatasetAssociation
file_size: 17
metadata_data_lines: 1
id: 6f91353f3eb0fa4a
misc_info: uploaded txt file
metadata_dbkey: ?
And now you've successfully used the API to request and select a history,
browse the contents of that history, and then look at detailed information
about a particular dataset.
For a more comprehensive Data Library example, set the following options in your
galaxy.yml as well, and restart galaxy again::
admin_users: you@example.org
library_import_dir: /path/to/some/directory
In the directory you specified for 'library_import_dir', create some
subdirectories, and put (or symlink) files to import into Galaxy into those
subdirectories.
In Galaxy, create an account that matches the address you put in 'admin_users',
then browse to that user's preferences and generate a new API Key. Copy the
key to your clipboard and then use these scripts::
% ./display.py my_key http://localhost:8080/api/libraries
Collection Members
------------------
0 elements in collection
% ./library_create_library.py my_key http://localhost:8080/api/libraries api_test 'API Test Library'
Response
--------
/api/libraries/f3f73e481f432006
name: api_test
id: f3f73e481f432006
% ./display.py my_key http://localhost:8080/api/libraries
Collection Members
------------------
/api/libraries/f3f73e481f432006
name: api_test
id: f3f73e481f432006
% ./display.py my_key http://localhost:8080/api/libraries/f3f73e481f432006
Member Information
------------------
synopsis: None
contents_url: /api/libraries/f3f73e481f432006/contents
description: API Test Library
name: api_test
% ./display.py my_key http://localhost:8080/api/libraries/f3f73e481f432006/contents
Collection Members
------------------
/api/libraries/f3f73e481f432006/contents/28202595c0d2591f61ddda595d2c3670
name: /
type: folder
id: 28202595c0d2591f61ddda595d2c3670
% ./library_create_folder.py my_key http://localhost:8080/api/libraries/f3f73e481f432006/contents 28202595c0d2591f61ddda595d2c3670 api_test_folder1 'API Test Folder 1'
Response
--------
/api/libraries/f3f73e481f432006/contents/28202595c0d2591fa4f9089d2303fd89
name: api_test_folder1
id: 28202595c0d2591fa4f9089d2303fd89
% ./library_upload_from_import_dir.py my_key http://localhost:8080/api/libraries/f3f73e481f432006/contents 28202595c0d2591fa4f9089d2303fd89 bed bed hg19
Response
--------
/api/libraries/f3f73e481f432006/contents/e9ef7fdb2db87d7b
name: 2.bed
id: e9ef7fdb2db87d7b
/api/libraries/f3f73e481f432006/contents/3b7f6a31f80a5018
name: 3.bed
id: 3b7f6a31f80a5018
% ./display.py my_key http://localhost:8080/api/libraries/f3f73e481f432006/contents
Collection Members
------------------
/api/libraries/f3f73e481f432006/contents/28202595c0d2591f61ddda595d2c3670
name: /
type: folder
id: 28202595c0d2591f61ddda595d2c3670
/api/libraries/f3f73e481f432006/contents/28202595c0d2591fa4f9089d2303fd89
name: /api_test_folder1
type: folder
id: 28202595c0d2591fa4f9089d2303fd89
/api/libraries/f3f73e481f432006/contents/e9ef7fdb2db87d7b
name: /api_test_folder1/2.bed
type: file
id: e9ef7fdb2db87d7b
/api/libraries/f3f73e481f432006/contents/3b7f6a31f80a5018
name: /api_test_folder1/3.bed
type: file
id: 3b7f6a31f80a5018
% ./display.py my_key http://localhost:8080/api/libraries/f3f73e481f432006/contents/e9ef7fdb2db87d7b
Member Information
------------------
misc_blurb: 68 regions
metadata_endCol: 3
data_type: bed
metadata_columns: 6
metadata_nameCol: 4
uploaded_by: nate@...
metadata_strandCol: 6
name: 2.bed
genome_build: hg19
metadata_comment_lines: None
metadata_startCol: 2
metadata_chromCol: 1
file_size: 4272
metadata_data_lines: 68
message:
metadata_dbkey: hg19
misc_info: uploaded bed file
date_uploaded: 2010-06-22T17:01:51.266119
metadata_column_types: str, int, int, str, int, str
Other parameters are valid when uploading, they are the same parameters as are
used in the web form, like 'link_data_only' and etc.
The request and response format should be considered alpha and are subject to change.
The Galaxy Training Network material has a tutorial designed
to teach using the Galaxy API at
https://training.galaxyproject.org/training-material/topics/dev/tutorials/bioblend-api/slides.html.
-3
View File
@@ -28,7 +28,4 @@ Overview of available API resources is available at https://galaxyproject.org/de
Quickstart <api/quickstart>
Development Guidelines <api/guidelines>
Documentation <api/api>
+172
View File
@@ -0,0 +1,172 @@
API Design Guidelines
=====================
The following section outlines guidelines related to extending and/or modifying
the Galaxy API. The Galaxy API has grown in an ad-hoc fashion over time by
many contributors and so clients SHOULD NOT expect the API will conform to
these guidelines - but developers contributing to the Galaxy API SHOULD follow
these guidelines.
Before Getting Started
~~~~~~~~~~~~~~~~~~~~~~
Developers should familiarize themselves with HTTP method definitions
https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9. While Galaxy
follows these imperfectly - a serious effort should be made to use HTTP
methods (GET, POST, PUT, DELETE, etc.) correctly and consistently. More
information about these methods with a focus on RESTful APIs can be found
at https://restfulapi.net/http-methods/.
Developers should familiarize themselves with the HTTP status code definitions
http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html. The API responses
should properly set the status code according to the result - in particular
2XX responses should be used for successful requests, 4XX for various
kinds of client errors, and 5XX for the errors on the server side.
Developers should also familiarize themselves with RESTful API design
https://docs.microsoft.com/en-us/azure/architecture/best-practices/api-design.
Again Galaxy follows generic RESTful guidelines imperfectly - but a serious
effort should be made to design the API as RESTfully and consistently as possible.
Documenting the API
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
An API endpoint implementation method should include high-level docstring documentation
for consumption at docs.galaxyproject.org.
API functionality should consume and produce Pydantic_ `models
<https://pydantic-docs.helpmanual.io/usage/models/>`__ with typed and annotated
`fields <https://pydantic-docs.helpmanual.io/usage/types/>`__. This information
will be used to generate detailed OpenAPI_ documentation for APIs in
the near future. Typed models also improve the quality and thoroughness
of the static checking that can be done for the Galaxy backend.
To further document your API and allow other developers to see how it functions,
new API functionality should include functional tests. These functional
tests should be implemented in Python and added to either the API test suite
or the integration test suite. Checkout the :ref:`Writing Tests for Galaxy <./writing_tests.md>`
documentation for more information on developing tests for Galaxy.
.. note::
The Galaxy documentation can be generated by running ``make docs-develop``
from the Galaxy root. Galaxy can be type checked by running ``tox -e mypy``
from the root of Galaxy after tox_ has been configured.
Error Handling
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
API methods should throw a finite number of exceptions
(defined in :mod:`galaxy.exceptions`) and these should subclass
:class:`galaxy.exceptions.MessageException` and not paste/WSGI/FastAPI HTTP exceptions.
When possible, the framework itself should be responsible for catching these exceptions,
setting the status code, and building an error response. Throwing these exceptions
will ensure the API responses are consistent across frameworks and across the API.
These exceptions allow throwers to attach arbitrary extra information.
Error responses should not consist of plain text strings - they should be
dictionaries describing the error and containing the following:
.. code-block: json
{
"status_code": 400,
"err_code": 400007,
"err_msg": "Request contained invalid parameter, action could not be completed.",
"type": "error",
"extra_error_info": "Extra information."
}
The error code (``err_code``) is defined by the exception class
and pulled from ``lig/galaxy/exceptions/error_codes.json``. This JSON
document should be used to add new error conditions or improve
default error messages.
If there is an error processing some part of the request (one item in a list
for instance), the status code should be set to reflect the error and the
partial result may or may not be returned depending on the controller -
this behavior should be documented.
Backward Compatibility
~~~~~~~~~~~~~~~~~~~~~~
Backward compatibility is important and should be maintained when possible.
If changing behavior in a non-backward compatible way please ensure one
of the following holds - there is a strong reason to believe no consumers
depend on a behavior, the behavior is effectively broken, or the API
method being modified has not been part of a tagged dist release. If these
conditions do not hold, please help the Galaxy release manager ensure
information about the breaking API is highlighted in the Galaxy release
notes.
Whenever functionality needs to be added, removed, or modified that
breaks backward compatibility in a significant way to a component used by
the community - a "dev" variant of the API should be established and
the community should be alerted and given a time frame for when the old
behavior will be replaced with the new behavior.
Design
~~~~~~
Functionality should not be copied and pasted between controllers -
consider refactoring functionality into associated classes or short of
that into mixins_ or into managers (:mod:`galaxy.managers`).
To whatever degree is possible, the Galaxy API controller classes
(found in :mod:`galaxy.webapps.galaxy.api`) should be as lean as
possible. Most of the logic should be pushed into components that
are decoupled from the actual web application and the web framework.
The role of the controller should be simply to adapt the web request
(tied to the web framework) to simple objects (plain Python objects,
Pydantic_ models, or SQL Alchemy models), send these objects to a
Python component lower in the stack, and finally to translate the
response to a web response. This design will ensure that Galaxy
application logic is usable in as many contexts as possible and
decouple the core "business logic" of Galaxy from the web framework
hosting it.
Going forward, we anticipate the most typical way this abstract
advice translates to concrete code flow is that API controllers
in :mod:`galaxy.webapps.galaxy.api` receive or produce Pydantic_
models and pass them to manager components defined in
:mod:`galaxy.managers`. The manager is then responsible for
interacting with the whole backend or specific components to
fullfil the desired request.
Galaxy work contexts provide a way for API requests (and other
components) to describe what they require from the framework
in terms of the request (examples include the Galaxy app, a user
to act on behalf of, or a user with a defined "current history").
These objects describing the web request are passed around rather
haphazardly as an argument named ``trans``. To design API methods
to deal with ``trans`` in the most statically checkable and
rigorous way - please review the docs for :mod:`galaxy.managers.context`.
Examples
~~~~~~~~
A very clean, isolated example of a well documented controller
and well annotated models is the licenses endpoint -
:mod:`galaxy.webapps.galaxy.api.licenses`. A more complicated
endpoint that demonstrates working with a manager and the
Galaxy model backend is the roles endpoint - :mod:`galaxy.webapps.galaxy.api.roles`.
BioBlend
~~~~~~~~
Changes to reflect modifications to the API should be pushed upstream to
the BioBlend_ project if possible.
Longer Term Goals
~~~~~~~~~~~~~~~~~~
- It would be advantageous to have a clearer separation of anonymous and
admin handling functionality.
- Consistent standards for range-based requests, batch requests, filtered
requests, etc... should be established and documented here.
.. _Pydantic: https://pydantic-docs.helpmanual.io/
.. _BioBlend: https://github.com/galaxyproject/bioblend
.. _mixins: http://en.wikipedia.org/wiki/Composition_over_inheritance
.. _tox: https://tox.readthedocs.io/en/latest/
.. _OpenAPI: https://www.openapis.org
+1
View File
@@ -12,6 +12,7 @@ A multi-hour long video playlist covering these slides can be found at
:maxdepth: 1
schema
api_guidelines
interactive_environments
build_a_job_runner
finding_and_improving_slow_code