Retire Packaging Deb project repos
This commit is part of a series to retire the Packaging Deb project. Step 2 is to remove all content from the project repos, replacing it with a README notification where to find ongoing work, and how to recover the repo if needed at some future point (as in https://docs.openstack.org/infra/manual/drivers.html#retiring-a-project). Change-Id: I1531aed08fc23dbcb04550285f9d41993cd10af4
This commit is contained in:
parent
a67ed2f235
commit
ace5ca6578
|
@ -1,7 +0,0 @@
|
|||
[run]
|
||||
branch = True
|
||||
source = os_api_ref
|
||||
omit = os_api_ref/openstack/*
|
||||
|
||||
[report]
|
||||
ignore_errors = True
|
|
@ -1,55 +0,0 @@
|
|||
*.py[cod]
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Packages
|
||||
*.egg*
|
||||
*.egg-info
|
||||
dist
|
||||
build
|
||||
eggs
|
||||
parts
|
||||
bin
|
||||
var
|
||||
sdist
|
||||
develop-eggs
|
||||
.installed.cfg
|
||||
lib
|
||||
lib64
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
cover/
|
||||
.coverage*
|
||||
!.coveragerc
|
||||
.tox
|
||||
nosetests.xml
|
||||
.testrepository
|
||||
.venv
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
|
||||
# Mr Developer
|
||||
.mr.developer.cfg
|
||||
.project
|
||||
.pydevproject
|
||||
|
||||
# Complexity
|
||||
output/*.html
|
||||
output/*/index.html
|
||||
|
||||
# Sphinx
|
||||
doc/build
|
||||
|
||||
# pbr generates these
|
||||
AUTHORS
|
||||
ChangeLog
|
||||
|
||||
# Editors
|
||||
*~
|
||||
.*.swp
|
||||
.*sw?
|
|
@ -1,4 +0,0 @@
|
|||
[gerrit]
|
||||
host=review.openstack.org
|
||||
port=29418
|
||||
project=openstack/os-api-ref.git
|
3
.mailmap
3
.mailmap
|
@ -1,3 +0,0 @@
|
|||
# Format is:
|
||||
# <preferred e-mail> <other e-mail 1>
|
||||
# <preferred e-mail> <other e-mail 2>
|
|
@ -1,7 +0,0 @@
|
|||
[DEFAULT]
|
||||
test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
|
||||
OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
|
||||
OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \
|
||||
${PYTHON:-python} -m subunit.run discover -t ./ . $LISTOPT $IDOPTION
|
||||
test_id_option=--load-list $IDFILE
|
||||
test_list_option=--list
|
|
@ -1,17 +0,0 @@
|
|||
If you would like to contribute to the development of OpenStack, you must
|
||||
follow the steps in this page:
|
||||
|
||||
http://docs.openstack.org/infra/manual/developers.html
|
||||
|
||||
If you already have a good understanding of how the system works and your
|
||||
OpenStack accounts are set up, you can skip to the development workflow
|
||||
section of this documentation to learn how changes to OpenStack should be
|
||||
submitted for review via the Gerrit tool:
|
||||
|
||||
http://docs.openstack.org/infra/manual/developers.html#development-workflow
|
||||
|
||||
Pull requests submitted through GitHub will be ignored.
|
||||
|
||||
Bugs should be filed on Launchpad, not GitHub:
|
||||
|
||||
https://bugs.launchpad.net/openstack-doc-tools
|
|
@ -1,4 +0,0 @@
|
|||
os-api-ref Style Commandments
|
||||
===============================================
|
||||
|
||||
Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/
|
176
LICENSE
176
LICENSE
|
@ -1,176 +0,0 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
include AUTHORS
|
||||
include ChangeLog
|
||||
exclude .gitignore
|
||||
exclude .gitreview
|
||||
|
||||
global-exclude *.pyc
|
|
@ -0,0 +1,14 @@
|
|||
This project is no longer maintained.
|
||||
|
||||
The contents of this repository are still available in the Git
|
||||
source code management system. To see the contents of this
|
||||
repository before it reached its end of life, please check out the
|
||||
previous commit with "git checkout HEAD^1".
|
||||
|
||||
For ongoing work on maintaining OpenStack packages in the Debian
|
||||
distribution, please see the Debian OpenStack packaging team at
|
||||
https://wiki.debian.org/OpenStack/.
|
||||
|
||||
For any further questions, please email
|
||||
openstack-dev@lists.openstack.org or join #openstack-dev on
|
||||
Freenode.
|
112
README.rst
112
README.rst
|
@ -1,112 +0,0 @@
|
|||
========================
|
||||
Team and repository tags
|
||||
========================
|
||||
|
||||
.. image:: http://governance.openstack.org/badges/os-api-ref.svg
|
||||
:target: http://governance.openstack.org/reference/tags/index.html
|
||||
|
||||
.. Change things from this point on
|
||||
|
||||
os-api-ref
|
||||
==========
|
||||
|
||||
Sphinx Extensions to support API reference sites in OpenStack
|
||||
|
||||
This project is a collection of sphinx stanzas that assist in building
|
||||
an API Reference site for an OpenStack project in RST. RST is great
|
||||
for unstructured English, but displaying semi structured (and
|
||||
repetitive) data in tables is not its strength. This provides tooling
|
||||
to insert semi-structured data describing request and response
|
||||
parameters and status or error messages, and turn those into nice tables.
|
||||
|
||||
The project also includes a set of styling (and javascript) that is
|
||||
expected to layer on top of a Sphinx theme base. This addition
|
||||
provides a nice set of collapsing sections for REST methods and
|
||||
javascript controls to expand / collapse all sections.
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
* Sphinx stanza ``rest_method`` describing the method and resource for a REST
|
||||
API call. Lets authors write simply and also gives readers a clean way to
|
||||
scan all methods then click a button to get more information about a method.
|
||||
* Sphinx stanza ``rest_parameters`` used to insert semi-structured data into
|
||||
the RST files describing the parameters users can send with the request. The
|
||||
stanza points to a structured YAML file, ``parameters.yaml``.
|
||||
* Sphinx stanza ``rest_status_code`` used to insert pointers to error or status
|
||||
codes returned by the service. Points to a structured YAML file,
|
||||
``http_codes.yaml``.
|
||||
|
||||
TODO
|
||||
----
|
||||
|
||||
A list, in no particular order, of things we should do in this
|
||||
project. If you would like to contribute to any of these please show
|
||||
up in ``#openstack-dev`` on IRC and ask for ``sdague`` or ``mugsie``
|
||||
to discuss or send an email to the openstack-dev@lists.openstack.org list
|
||||
with [api] in the subject line.
|
||||
|
||||
* Enhance documentation with more examples and best practices
|
||||
* Testing for the code
|
||||
* ``max_microversion`` parameter support - so that we automatically
|
||||
tag parameters that have been removed
|
||||
* Make a microversion selector, so that you can get a version of the api-ref
|
||||
that hides all microversion elements beyond your selected version
|
||||
(this one is going to be a bit of complex javascript), in progress.
|
||||
|
||||
Potential ideas
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
These aren't even quite todos, but just ideas about things that might
|
||||
be useful.
|
||||
|
||||
* ``.. literalinclude`` is good for API samples files to be included,
|
||||
but should we have more markup that includes the full ``REST /URL``
|
||||
as well.
|
||||
|
||||
|
||||
Sphinx stanzas
|
||||
--------------
|
||||
|
||||
**rest_method**: Enter the REST method, such as GET, PUT, POST, DELETE,
|
||||
followed by the resource (not including an endpoint) for the call. For
|
||||
example::
|
||||
|
||||
.. rest_method:: PUT /v2/images/{image_id}/file
|
||||
|
||||
**rest_parameters**: Enter a reference to a ``parameters.yaml`` file and
|
||||
indicate which parameter you want to document. For example::
|
||||
|
||||
.. rest_parameters:: images-parameters.yaml
|
||||
|
||||
- Content-type: Content-Type-data
|
||||
- image_id: image_id-in-path
|
||||
|
||||
Where the ``images-parameters.yaml`` file contains pointers named
|
||||
``Content-type`` and ``image_id`` and descriptions for each.
|
||||
|
||||
**rest_status_code**: Enter a reference to a ``http-status.yaml`` file and
|
||||
indicate which errors or status codes you want to document. You can also add
|
||||
a pointer to more precise descriptions for each code. For example::
|
||||
|
||||
.. rest_status_code:: success http-codes.yaml
|
||||
|
||||
- 204
|
||||
|
||||
.. rest_status_code:: error http-codes.yaml
|
||||
|
||||
- 400: informal
|
||||
- 401
|
||||
- 403
|
||||
- 404
|
||||
- 409
|
||||
- 410: image-data-410
|
||||
- 413: image-data-413
|
||||
- 415: image-data-415
|
||||
- 500: informal
|
||||
- 503: image-data-503
|
||||
|
||||
|
||||
* Free software: Apache license
|
||||
* Documentation: http://docs.openstack.org/developer/os-api-ref
|
||||
* Source: http://git.openstack.org/cgit/openstack/os-api-ref
|
|
@ -1,41 +0,0 @@
|
|||
===========
|
||||
Releasing
|
||||
===========
|
||||
|
||||
This documents the how and when to release ``os-api-ref``.
|
||||
|
||||
When to release
|
||||
===============
|
||||
|
||||
Any time there are fixes or additions ready to go, they should be
|
||||
released. Releases are cheap. If it's been more than a month and there
|
||||
are changes in master, consider releasing them.
|
||||
|
||||
If the changes are entirely on the CSS / JS cosmetic side, things are
|
||||
usually pretty safe to release as long as they have been spot checked
|
||||
against a couple of projects. (The gate does the nova tree
|
||||
automatically).
|
||||
|
||||
If new warnings are added
|
||||
-------------------------
|
||||
|
||||
If **new** warnings have been added since the last release, care
|
||||
should be taken to:
|
||||
|
||||
* Alert the mailing list 2 days before the release about the new
|
||||
warning coming in (that should give them time to go non enforcing
|
||||
or fix the issue).
|
||||
* Ensure that you bump at least the Y in the version number
|
||||
(X.Y.Z). New warnings are not a Z level release.
|
||||
|
||||
How to release
|
||||
==============
|
||||
|
||||
Check out ``openstack/releases``
|
||||
|
||||
Edit ``deliverables/_independent/os-api-ref.yaml``
|
||||
|
||||
Add a line with the version number desired, the git has of the commit
|
||||
that should be that release.
|
||||
|
||||
If you have questions ask in ``#openstack-release``
|
14
TESTS.rst
14
TESTS.rst
|
@ -1,14 +0,0 @@
|
|||
==============================
|
||||
Tests that should be written
|
||||
==============================
|
||||
|
||||
Negative
|
||||
|
||||
* test warning when groups are out of order
|
||||
* test when parameter keys are missing
|
||||
* test when description RST is invalid
|
||||
* test when parameter file is missing
|
||||
|
||||
Microversion
|
||||
|
||||
* test attributes are set when microversions are annotated.
|
|
@ -1,74 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.abspath('../..'))
|
||||
# -- General configuration ----------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = [
|
||||
'sphinx.ext.autodoc',
|
||||
#'sphinx.ext.intersphinx',
|
||||
'oslosphinx'
|
||||
]
|
||||
|
||||
# autodoc generation is a bit aggressive and a nuisance when doing heavy
|
||||
# text edit cycles.
|
||||
# execute "export SPHINX_DEBUG=1" in your terminal to disable
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'os-api-ref'
|
||||
copyright = u'2016, The contributors'
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
add_module_names = True
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# -- Options for HTML output --------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
||||
# Sphinx are currently 'default' and 'sphinxdoc'.
|
||||
# html_theme_path = ["."]
|
||||
# html_theme = '_theme'
|
||||
# html_static_path = ['static']
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = '%sdoc' % project
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass
|
||||
# [howto/manual]).
|
||||
latex_documents = [
|
||||
('index',
|
||||
'%s.tex' % project,
|
||||
u'%s Documentation' % project,
|
||||
u'OpenStack Foundation', 'manual'),
|
||||
]
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
#intersphinx_mapping = {'http://docs.python.org/': None}
|
|
@ -1,4 +0,0 @@
|
|||
|
||||
Contributing
|
||||
============
|
||||
.. include:: ../../CONTRIBUTING.rst
|
|
@ -1,63 +0,0 @@
|
|||
200:
|
||||
default: |
|
||||
Request was successful.
|
||||
image-data-200: |
|
||||
The service lists the image data in the response body.
|
||||
201:
|
||||
default: |
|
||||
Request has been fulfilled and new resource created.
|
||||
202:
|
||||
default: |
|
||||
Request is accepted, but processing may take some time.
|
||||
203:
|
||||
default: |
|
||||
Returned information is not full set, but a subset.
|
||||
204:
|
||||
default: |
|
||||
Request fulfilled but service does not return anything.
|
||||
300:
|
||||
default: |
|
||||
The resource corresponds to more than one representation.
|
||||
400:
|
||||
default: |
|
||||
Some content in the request was invalid.
|
||||
401:
|
||||
default: |
|
||||
User must authenticate before making a request.
|
||||
403:
|
||||
default: |
|
||||
Policy does not allow current user to do this operation.
|
||||
404:
|
||||
default: |
|
||||
The requested resource could not be found.
|
||||
405:
|
||||
default: |
|
||||
Method is not valid for this endpoint and resource.
|
||||
409:
|
||||
default: |
|
||||
This resource has an action in progress that would conflict with this request.
|
||||
413:
|
||||
default: |
|
||||
This operation cannot be completed.
|
||||
image-data-413: |
|
||||
The payload cannot be accepted. Possible causes include:
|
||||
* The backend storage is full.
|
||||
* This request added to your existing image data exceeds your total
|
||||
storage quota for images.
|
||||
* The image payload submitted with this request exceeds the maximum
|
||||
allowable image size.
|
||||
415:
|
||||
default: |
|
||||
The entity of the request is in a format not supported by the requested
|
||||
resource for the method.
|
||||
500:
|
||||
default: |
|
||||
Something went wrong with the service which prevents it from fulfilling
|
||||
the request.
|
||||
501:
|
||||
default: |
|
||||
The service does not have the functionality required to fulfill this
|
||||
request.
|
||||
503:
|
||||
default: |
|
||||
The service cannot handle the request right now.
|
|
@ -1,23 +0,0 @@
|
|||
.. os-api-ref documentation master file, created by
|
||||
sphinx-quickstart on Tue Jul 9 22:26:36 2013.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
==============================================
|
||||
Welcome to os-api-ref developer documentation!
|
||||
==============================================
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
installation
|
||||
usage
|
||||
contributing
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
|
@ -1,11 +0,0 @@
|
|||
Installation
|
||||
============
|
||||
|
||||
At the command line::
|
||||
|
||||
$ pip install os-api-ref
|
||||
|
||||
Or, if you have virtualenvwrapper installed::
|
||||
|
||||
$ mkvirtualenv os-api-ref
|
||||
$ pip install os-api-ref
|
|
@ -1,340 +0,0 @@
|
|||
Usage
|
||||
=====
|
||||
|
||||
``os-api-ref`` is designed to be used inside of a sphinx tree that is
|
||||
devoted solely to the documentation of the API.
|
||||
|
||||
Modify your ``source/conf.py`` file to include ``os_api_ref`` in the
|
||||
list of sphinx extensions. This extension assumes you are also using
|
||||
``openstackdocstheme`` for some of the styling, and may not fully work if you
|
||||
are not.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
|
||||
extensions = [
|
||||
'os_api_ref',
|
||||
]
|
||||
|
||||
|
||||
Stanzas
|
||||
=======
|
||||
|
||||
rest_method
|
||||
-----------
|
||||
|
||||
The ``rest_method`` stanza is a way to declare that a section is about
|
||||
a particular REST method. It takes the form of:
|
||||
|
||||
.. code-block:: rst
|
||||
|
||||
.. rest_method:: <METHODNAME> <url>
|
||||
|
||||
``METHODNAME`` should be one of the commonly used REST methods or HTTP verbs.
|
||||
This stanza should be the first element in a ``section`` that has some
|
||||
descriptive title about the method. An example from the Nova
|
||||
documentation is:
|
||||
|
||||
.. code-block:: rst
|
||||
|
||||
List Servers
|
||||
============
|
||||
|
||||
.. rest_method:: GET /v2.1/{tenant_id}/servers
|
||||
|
||||
Lists IDs, names, and links for all servers.
|
||||
|
||||
|
||||
Servers contain a status attribute that indicates the current server
|
||||
state. You can filter on the server status when you complete a list
|
||||
servers request. The server status is returned in the response
|
||||
body. The possible server status values are:
|
||||
|
||||
...
|
||||
|
||||
This is going to do a slightly unexpected transform where the
|
||||
``rest_method`` is pivoted up and into the section title to produce an
|
||||
HTML line of the form: <METHOD> <URL> <SECTIONTITLE> <SHOW/HIDE
|
||||
BUTTON>.
|
||||
|
||||
The entire contents of the ``List Servers`` section will then be
|
||||
hidden by default, with a button to open it on demand.
|
||||
|
||||
rest_parameters
|
||||
---------------
|
||||
|
||||
The ``rest_parameters`` stanza is a solution to the problem of tables
|
||||
in ``rst``.
|
||||
|
||||
A REST API that uses JSON has a large number of structured parameters
|
||||
that include type, location (i.e. is this in the query, the header,
|
||||
the path, the body), whether or not this parameter is required, as well as
|
||||
the desire to provide a long description about each parameter. And, assuming
|
||||
some consistent modeling, that parameter will show up in multiple calls. A
|
||||
``server_id`` used in the path is always going to have the same
|
||||
meaning.
|
||||
|
||||
It is natural to want to display this data in a tabular way to show
|
||||
all these dimensions. However, tables in ``rst`` are quite cumbersome, and
|
||||
repeating the same data over and over again is error prone.
|
||||
|
||||
The ``rest_parameters`` stanza solves this by having the inline markup
|
||||
be a yaml list of ``name: value`` pairs. ``name`` is the name of the
|
||||
parameter. ``value`` is the key to lookup the rest of the details for
|
||||
this parameter in a parameters file. In each method,
|
||||
there should be one ``rest_parameters`` stanza for the request, and
|
||||
another ``rest_parameters`` stanza for the response.
|
||||
|
||||
.. code-block:: rst
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- tenant_id: tenant_id
|
||||
- changes-since: changes-since
|
||||
- image: image_query
|
||||
- flavor: flavor_query
|
||||
- name: server_name_query
|
||||
- status: server_status_query
|
||||
- host: host_query
|
||||
- limit: limit
|
||||
- marker: marker
|
||||
|
||||
And corresponding entries in ``parameters.yaml``:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
tenant_id:
|
||||
description: |
|
||||
The UUID of the tenant in a multi-tenancy cloud.
|
||||
in: path
|
||||
required: true
|
||||
type: string
|
||||
...
|
||||
changes-since:
|
||||
description: |
|
||||
Filters the response by a date and time when the image last changed status.
|
||||
Use this query parameter to check for changes since a previous request rather
|
||||
than re-downloading and re-parsing the full status at each polling interval.
|
||||
If data has changed, the call returns only the items changed since the ``changes-since``
|
||||
time. If data has not changed since the ``changes-since`` time, the call returns an
|
||||
empty list.\nTo enable you to keep track of changes, this filter also displays images
|
||||
that were deleted if the ``changes-since`` value specifies a date in the last 30 days.
|
||||
Items deleted more than 30 days ago might be returned, but it is not guaranteed.
|
||||
The date and time stamp format is `ISO 8601 <https://en.wikipedia.org/wiki/ISO_8601>`_:
|
||||
|
||||
::
|
||||
|
||||
CCYY-MM-DDThh:mm:ss±hh:mm
|
||||
|
||||
The ``±hh:mm`` value, if included, returns the time zone as an offset from UTC.
|
||||
For example, ``2015-08-27T09:49:58-05:00``.
|
||||
If you omit the time zone, the UTC time zone is assumed.
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
server_status_query:
|
||||
description: |
|
||||
Filters the response by a server status, as a string. For example, ``ACTIVE``.
|
||||
in: query
|
||||
required: false
|
||||
type: string
|
||||
|
||||
Every ``rest_parameters`` stanza specifies the lookup file it will
|
||||
use. This gives you the freedom to decide how you would like to split
|
||||
up your parameters, ranging from a single global file, to a dedicated
|
||||
file for every stanza, or anywhere in between.
|
||||
|
||||
parameters file format
|
||||
----------------------
|
||||
|
||||
The parameters file is inspired by the OpenAPI (aka: Swagger)
|
||||
specification. The OpenAPI specification provides a property object
|
||||
which categorizes the parameters by type and describes how the parameter is used.
|
||||
The following fields exist for every entry:
|
||||
|
||||
in
|
||||
where this parameter exists. One of ``header``, ``path``,
|
||||
``query``, ``body``.
|
||||
|
||||
description
|
||||
a free form description of the parameter. This can be
|
||||
multiline (if using the | or > tags in yaml), and supports ``rst``
|
||||
format syntax.
|
||||
|
||||
required
|
||||
whether this parameter is required or not. If ``required:
|
||||
false`` the parameter name will be rendered with an (Optional)
|
||||
keyword next to it
|
||||
|
||||
type
|
||||
the javascript/json type of the field. one of ``boolean``, ``int``,
|
||||
``float``, ``string``, ``array``, ``object``.
|
||||
|
||||
min_version
|
||||
the microversion that this parameter was introduced at. Will render
|
||||
a *new in $version* stanza in the html output.
|
||||
|
||||
max_version
|
||||
the last version that includes this parameter. Will render
|
||||
a *Deprecated in $version* stanza in the html output.
|
||||
|
||||
|
||||
rest_status_code
|
||||
----------------
|
||||
|
||||
The ``rest_status_code`` stanza is how you can show what HTTP status codes your
|
||||
API uses and what they indicate.
|
||||
|
||||
.. code-block:: rst
|
||||
|
||||
.. rest_status_code:: <success|error> <location of http-status.yaml file>
|
||||
|
||||
This stanza should be the first element after the narrative section of the
|
||||
method description.
|
||||
|
||||
An example from the Designate documentation is:
|
||||
|
||||
.. code-block:: rst
|
||||
:emphasize-lines: 11-25
|
||||
|
||||
Create Zone
|
||||
===========
|
||||
|
||||
.. rest_method:: POST /v2/zones
|
||||
|
||||
Create a zone
|
||||
|
||||
Response codes
|
||||
--------------
|
||||
|
||||
.. rest_status_code:: success status.yaml
|
||||
|
||||
- 200
|
||||
- 100
|
||||
- 201
|
||||
|
||||
|
||||
.. rest_status_code:: error status.yaml
|
||||
|
||||
- 405
|
||||
- 403
|
||||
- 401
|
||||
- 400
|
||||
- 500
|
||||
- 409: duplicate_zone
|
||||
|
||||
And corresponding entries in ``http-status.yaml``:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
100:
|
||||
default: |
|
||||
An unusual code for an API
|
||||
200:
|
||||
default: |
|
||||
Request was successful.
|
||||
201:
|
||||
default: >
|
||||
Request has been fulfilled and new resource created. The ``Location`` header
|
||||
has the URL to the new item.
|
||||
400:
|
||||
default: |
|
||||
Some content in the request was invalid
|
||||
zone_data_error: |
|
||||
Some of the data for the zone in the request is unavailable to the service.
|
||||
401:
|
||||
default: |
|
||||
User must authenticate before making a request.
|
||||
403:
|
||||
default: |
|
||||
Policy does not allow current user to do this operation.
|
||||
405:
|
||||
default: |
|
||||
Method is not valid for this endpoint and resource.
|
||||
409:
|
||||
default: |
|
||||
This resource has an action in progress that would conflict with this request.
|
||||
duplicate_zone: |
|
||||
There is already a zone with this name.
|
||||
500:
|
||||
default: |
|
||||
Something went wrong with the service which prevents it from fulfilling the request.
|
||||
|
||||
This RST example creates two HTML tables of response codes, one for success and one for
|
||||
errors.
|
||||
|
||||
status file format
|
||||
------------------
|
||||
|
||||
This is a simple yaml file, with a single object of status codes and the
|
||||
reasons that each would be used.
|
||||
|
||||
Each status code **must** have a default entry in the status yaml file. The default entry is used
|
||||
in the ``rest_status_code`` stanza when a code is listed with no value or lookup key.
|
||||
|
||||
There may be situations where the reason for a code may be different across
|
||||
endpoints, or a different message may be appropriate.
|
||||
|
||||
In this case, adding a entry at the same level as the ``default`` and
|
||||
referencing that in the stanza like so:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
- 409: duplicate_zone
|
||||
|
||||
This will override the default message with the newly defined one.
|
||||
|
||||
You can get a copy of a starter status file from the os-api-ref repository,
|
||||
by downloading :download:`http-status.yaml <http-status.yaml>`.
|
||||
|
||||
rest_expand_all
|
||||
---------------
|
||||
|
||||
The ``rest_expand_all`` stanza is used to place a control in the
|
||||
document that will be a global Show / Hide for all sections. There are
|
||||
times when this is extremely nice to have.
|
||||
|
||||
|
||||
Including Sample Files
|
||||
======================
|
||||
|
||||
To refer to a sample file in a ``rst`` file, use the
|
||||
``rst`` directive, ``literalinclude``. Typically, the content sent
|
||||
or received is of type JSON, so the language role is set to javascript.
|
||||
The example immediately follows the parameter listing in the ``rst`` file.
|
||||
An example of an included Nova response sample file:
|
||||
|
||||
.. code-block:: rst
|
||||
|
||||
.. literalinclude:: ../../doc/api_samples/os-evacuate/server-evacuate-resp.json
|
||||
:language: javascript
|
||||
|
||||
|
||||
Runtime Warnings
|
||||
================
|
||||
|
||||
The extension tries to help when it can by pointing out that something isn't
|
||||
matching up correctly. The following warnings are generated when
|
||||
issues are found:
|
||||
|
||||
* parameters file is not found
|
||||
* parameters file is not valid yaml, i.e.
|
||||
missing colon after the name
|
||||
* a lookup value in the ``rst`` file is not found in the parameters file
|
||||
* the parameters file is not sorted as outlined in the rules below
|
||||
|
||||
The sorting rules for parameters file is that first elements should be
|
||||
sorted by ``in``, going from earliest to latest processed.
|
||||
|
||||
#. header
|
||||
#. path
|
||||
#. query
|
||||
#. body
|
||||
|
||||
After that, the parameters should be sorted by name, lower case alpha
|
||||
numerically.
|
||||
|
||||
The sort enforcement is because in large parameters files it helps
|
||||
prevent unintended duplicates.
|
|
@ -1,705 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from collections import OrderedDict
|
||||
import os
|
||||
import re
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.parsers.rst.directives.tables import Table
|
||||
from docutils.statemachine import ViewList
|
||||
import pbr.version
|
||||
from sphinx.util.compat import Directive
|
||||
from sphinx.util.osutil import copyfile
|
||||
import yaml
|
||||
|
||||
from os_api_ref.http_codes import http_code
|
||||
from os_api_ref.http_codes import http_code_html
|
||||
from os_api_ref.http_codes import HTTPResponseCodeDirective
|
||||
|
||||
__version__ = pbr.version.VersionInfo(
|
||||
'os_api_ref').version_string()
|
||||
|
||||
# This is to allow for a graceful swap from oslosphinx to openstackdocstheme
|
||||
THEME = 'openstackdocstheme'
|
||||
|
||||
|
||||
"""This provides a sphinx extension able to create the HTML needed
|
||||
for the api-ref website.
|
||||
|
||||
It contains 2 new stanzas.
|
||||
|
||||
.. rest_method:: GET /foo/bar
|
||||
|
||||
Which is designed to be used as the first stanza in a new section to
|
||||
state that section is about that REST method. During processing the
|
||||
rest stanza will be reparented to be before the section in question,
|
||||
and used as a show/hide selector for it's details.
|
||||
|
||||
.. rest_parameters:: file.yaml
|
||||
|
||||
- name1: name_in_file1
|
||||
- name2: name_in_file2
|
||||
- name3: name_in_file3
|
||||
|
||||
Which is designed to build structured tables for either response or
|
||||
request parameters. The stanza takes a value which is a file to lookup
|
||||
details about the parameters in question.
|
||||
|
||||
The contents of the stanza are a yaml list of key / value pairs. The
|
||||
key is the name of the parameter to be shown in the table. The value
|
||||
is the key in the file.yaml where all other metadata about the
|
||||
parameter will be extracted. This allows for reusing parameter
|
||||
definitions widely in API definitions, but still providing for control
|
||||
in both naming and ordering of parameters at every declaration.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def ordered_load(
|
||||
stream, Loader=yaml.SafeLoader, object_pairs_hook=OrderedDict):
|
||||
"""Load yaml as an ordered dict
|
||||
|
||||
This allows us to inspect the order of the file on disk to make
|
||||
sure it was correct by our rules.
|
||||
"""
|
||||
class OrderedLoader(Loader):
|
||||
pass
|
||||
|
||||
def construct_mapping(loader, node):
|
||||
loader.flatten_mapping(node)
|
||||
return object_pairs_hook(loader.construct_pairs(node))
|
||||
|
||||
OrderedLoader.add_constructor(
|
||||
yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
|
||||
construct_mapping)
|
||||
# for parameters.yaml we treat numbers (especially version
|
||||
# numbers) as strings. So that microversion specification of 2.20
|
||||
# and 2.2 don't get confused.
|
||||
OrderedLoader.add_constructor(
|
||||
u'tag:yaml.org,2002:float',
|
||||
yaml.constructor.SafeConstructor.construct_yaml_str)
|
||||
|
||||
return yaml.load(stream, OrderedLoader)
|
||||
|
||||
|
||||
class rest_method(nodes.Part, nodes.Element):
|
||||
"""Node for rest_method stanza
|
||||
|
||||
Because we need to insert very specific HTML at the final stage of
|
||||
processing, the rest_method stanza needs a custom node type. This
|
||||
lets us accumulate the relevant data into this node, during
|
||||
parsing, but not turn it into known sphinx types (lists, tables,
|
||||
sections).
|
||||
|
||||
Then, during the final build phase we transform directly to the
|
||||
html that we want.
|
||||
|
||||
NOTE: this means we error trying to build latex or man pages for
|
||||
these stanza types right now. This is all fixable if we add an
|
||||
output formatter for this node type, but it's not yet a
|
||||
priority. Contributions welcomed.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class rest_expand_all(nodes.Part, nodes.Element):
|
||||
"""A node placeholder for the expand all button.
|
||||
|
||||
This is a node that we can insert into the doctree which on final
|
||||
render can be converted to the custom HTML we need for the expand
|
||||
all button. It is automatically inserted at the top of the page
|
||||
for API ref documents.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class RestExpandAllDirective(Directive):
|
||||
# This tells sphinx that the directive will need to generate
|
||||
# content during the final build phase.
|
||||
has_content = True
|
||||
|
||||
def run(self):
|
||||
app = self.state.document.settings.env.app
|
||||
node = rest_expand_all()
|
||||
max_ver = app.config.os_api_ref_max_microversion
|
||||
min_ver = app.config.os_api_ref_min_microversion
|
||||
releases = app.config.os_api_ref_release_microversions
|
||||
node['major'] = None
|
||||
try:
|
||||
if max_ver.split('.')[0] == min_ver.split('.')[0]:
|
||||
node['max_ver'] = int(max_ver.split('.')[1])
|
||||
node['min_ver'] = int(min_ver.split('.')[1])
|
||||
node['major'] = int(max_ver.split('.')[0])
|
||||
node['releases'] = releases
|
||||
except ValueError:
|
||||
# TODO(sdague): warn that we're ignoring this all
|
||||
pass
|
||||
except IndexError:
|
||||
pass
|
||||
return [node]
|
||||
|
||||
|
||||
class RestMethodDirective(Directive):
|
||||
|
||||
# this enables content in the directive
|
||||
has_content = True
|
||||
|
||||
@staticmethod
|
||||
def find_param(content, name):
|
||||
for line in content:
|
||||
if ("%s: " % name) in line:
|
||||
_, value = line.split(': ')
|
||||
return value.rstrip().lstrip()
|
||||
return None
|
||||
|
||||
def run(self):
|
||||
lineno = self.state_machine.abs_line_number()
|
||||
target = nodes.target()
|
||||
section = nodes.section(classes=["detail-control"])
|
||||
# env = self.state.document.settings.env
|
||||
# env.app.info("Parent %s" % self.state.parent.attributes)
|
||||
|
||||
node = rest_method()
|
||||
|
||||
# TODO(sdague): this is a super simplistic parser, should be
|
||||
# more robust.
|
||||
method, sep, url = self.content[0].partition(' ')
|
||||
node['min_version'] = self.find_param(self.content, 'min_version')
|
||||
node['max_version'] = self.find_param(self.content, 'max_version')
|
||||
|
||||
node['method'] = method
|
||||
node['url'] = url
|
||||
|
||||
# Extract the path parameters from the url
|
||||
env = self.state.document.settings.env
|
||||
env.path_params = []
|
||||
env.path_params = re.findall("{[a-zA-Z][a-zA-Z_0-9]*}", url)
|
||||
|
||||
node['target'] = self.state.parent.attributes['ids'][0]
|
||||
node['css_classes'] = ""
|
||||
if node['min_version']:
|
||||
node['css_classes'] += "rp_min_ver_%s " % (
|
||||
str(node['min_version']).replace('.', '_'))
|
||||
if node['max_version']:
|
||||
node['css_classes'] += "rp_max_ver_%s " % (
|
||||
str(node['max_version']).replace('.', '_'))
|
||||
|
||||
# We need to build a temporary target that we can replace
|
||||
# later in the processing to get the TOC to resolve correctly.
|
||||
temp_target = "%s-selector" % node['target']
|
||||
target = nodes.target(ids=[temp_target])
|
||||
self.state.add_target(temp_target, '', target, lineno)
|
||||
section += node
|
||||
|
||||
return [target, section]
|
||||
|
||||
# cache for file -> yaml so we only do the load and check of a yaml
|
||||
# file once during a sphinx processing run.
|
||||
YAML_CACHE = {}
|
||||
|
||||
|
||||
class RestParametersDirective(Table):
|
||||
|
||||
headers = ["Name", "In", "Type", "Description"]
|
||||
|
||||
def _load_param_file(self, fpath):
|
||||
global YAML_CACHE
|
||||
if fpath in YAML_CACHE:
|
||||
return YAML_CACHE[fpath]
|
||||
|
||||
lookup = {}
|
||||
# self.app.info("Fpath: %s" % fpath)
|
||||
try:
|
||||
with open(fpath, 'r') as stream:
|
||||
lookup = ordered_load(stream)
|
||||
except IOError:
|
||||
self.app.warn(
|
||||
"Parameters file %s not found" % fpath,
|
||||
(self.env.docname, None))
|
||||
return
|
||||
except yaml.YAMLError as exc:
|
||||
self.app.warn(exc)
|
||||
raise
|
||||
|
||||
if lookup:
|
||||
self._check_yaml_sorting(fpath, lookup)
|
||||
else:
|
||||
self.app.warn(
|
||||
"Parameters file is empty %s" % fpath,
|
||||
(self.env.docname, None))
|
||||
return
|
||||
|
||||
YAML_CACHE[fpath] = lookup
|
||||
return lookup
|
||||
|
||||
def _check_yaml_sorting(self, fpath, yaml_data):
|
||||
"""check yaml sorting
|
||||
|
||||
Assuming we got an ordered dict, we iterate through it
|
||||
basically doing a gnome sort test
|
||||
(https://en.wikipedia.org/wiki/Gnome_sort) and ensure the item
|
||||
we are looking at is > the last item we saw. This is done at
|
||||
the section level first, so we're grouped, then alphabetically
|
||||
by lower case name within a section. Every time there is a
|
||||
mismatch we raise an warn message.
|
||||
"""
|
||||
sections = {"header": 1, "path": 2, "query": 3, "body": 4}
|
||||
|
||||
last = None
|
||||
for key, value in yaml_data.items():
|
||||
# use of an invalid 'in' value
|
||||
if value['in'] not in sections:
|
||||
self.app.warn(
|
||||
"``%s`` is not a valid value for 'in' (must be one of: %s)"
|
||||
". (see ``%s``)" % (
|
||||
value['in'],
|
||||
", ".join(sorted(sections.keys())),
|
||||
key)
|
||||
)
|
||||
continue
|
||||
|
||||
if last is None:
|
||||
last = (key, value)
|
||||
continue
|
||||
# ensure that sections only go up
|
||||
current_section = value['in']
|
||||
last_section = last[1]['in']
|
||||
if sections[current_section] < sections[last_section]:
|
||||
self.app.warn(
|
||||
"Section out of order. All parameters in section ``%s`` "
|
||||
"should be after section ``%s``. (see ``%s``)" % (
|
||||
last_section,
|
||||
current_section,
|
||||
last[0]))
|
||||
if (sections[value['in']] == sections[last[1]['in']] and
|
||||
key.lower() < last[0].lower()):
|
||||
self.app.warn(
|
||||
"Parameters out of order ``%s`` should be after ``%s``" % (
|
||||
last[0], key))
|
||||
last = (key, value)
|
||||
|
||||
def yaml_from_file(self, fpath):
|
||||
"""Collect Parameter stanzas from inline + file.
|
||||
|
||||
This allows use to reference an external file for the actual
|
||||
parameter definitions.
|
||||
"""
|
||||
|
||||
lookup = self._load_param_file(fpath)
|
||||
if not lookup:
|
||||
return
|
||||
|
||||
content = "\n".join(self.content)
|
||||
parsed = yaml.safe_load(content)
|
||||
# self.app.info("Params loaded is %s" % parsed)
|
||||
# self.app.info("Lookup table looks like %s" % lookup)
|
||||
new_content = list()
|
||||
for paramlist in parsed:
|
||||
if not isinstance(paramlist, dict):
|
||||
self.app.warn(
|
||||
("Invalid parameter definition ``%s``. Expected "
|
||||
"format: ``name: reference``. "
|
||||
" Skipping." % paramlist),
|
||||
(self.state_machine.node.source,
|
||||
self.state_machine.node.line))
|
||||
continue
|
||||
for name, ref in paramlist.items():
|
||||
if ref in lookup:
|
||||
new_content.append((name, lookup[ref]))
|
||||
else:
|
||||
# TODO(sdague): this provides a kind of confusing
|
||||
# error message because app.warn isn't meant to be
|
||||
# used this way, however it does provide a way to
|
||||
# track down where the parameters list is that is
|
||||
# wrong. So it's good enough for now.
|
||||
self.app.warn(
|
||||
("No field definition for ``%s`` found in ``%s``. "
|
||||
" Skipping." % (ref, fpath)),
|
||||
(self.state_machine.node.source,
|
||||
self.state_machine.node.line))
|
||||
|
||||
# Check for path params in stanza
|
||||
for i, param in enumerate(self.env.path_params):
|
||||
if (param.rstrip('}').lstrip('{')) == name:
|
||||
del self.env.path_params[i]
|
||||
break
|
||||
else:
|
||||
continue
|
||||
|
||||
if len(self.env.path_params) is not 0:
|
||||
# Warn that path parameters are not set in rest_parameter
|
||||
# stanza and will not appear in the generated table.
|
||||
for param in self.env.path_params:
|
||||
self.app.warn(
|
||||
("No path parameter ``%s`` found in rest_parameter"
|
||||
" stanza.\n"
|
||||
% param.rstrip('}').lstrip('{')),
|
||||
(self.state_machine.node.source,
|
||||
self.state_machine.node.line))
|
||||
|
||||
# self.app.info("New content %s" % new_content)
|
||||
self.yaml = new_content
|
||||
|
||||
def run(self):
|
||||
self.env = self.state.document.settings.env
|
||||
self.app = self.env.app
|
||||
|
||||
# Make sure we have some content, which should be yaml that
|
||||
# defines some parameters.
|
||||
if not self.content:
|
||||
error = self.state_machine.reporter.error(
|
||||
'No parameters defined',
|
||||
nodes.literal_block(self.block_text, self.block_text),
|
||||
line=self.lineno)
|
||||
return [error]
|
||||
|
||||
if not len(self.arguments) >= 1:
|
||||
error = self.state_machine.reporter.error(
|
||||
'No reference file defined',
|
||||
nodes.literal_block(self.block_text, self.block_text),
|
||||
line=self.lineno)
|
||||
return [error]
|
||||
|
||||
# NOTE(sdague): it's important that we pop the arg otherwise
|
||||
# we end up putting the filename as the table caption.
|
||||
rel_fpath, fpath = self.env.relfn2path(self.arguments.pop())
|
||||
self.yaml_file = fpath
|
||||
self.yaml_from_file(self.yaml_file)
|
||||
|
||||
self.max_cols = len(self.headers)
|
||||
# TODO(sdague): it would be good to dynamically set column
|
||||
# widths (or basically make the colwidth thing go away
|
||||
# entirely)
|
||||
self.options['widths'] = [20, 10, 10, 60]
|
||||
self.col_widths = self.get_column_widths(self.max_cols)
|
||||
if isinstance(self.col_widths, tuple):
|
||||
# In docutils 0.13.1, get_column_widths returns a (widths,
|
||||
# colwidths) tuple, where widths is a string (i.e. 'auto').
|
||||
# See https://sourceforge.net/p/docutils/patches/120/.
|
||||
self.col_widths = self.col_widths[1]
|
||||
# Actually convert the yaml
|
||||
title, messages = self.make_title()
|
||||
# self.app.info("Title %s, messages %s" % (title, messages))
|
||||
table_node = self.build_table()
|
||||
self.add_name(table_node)
|
||||
if title:
|
||||
table_node.insert(0, title)
|
||||
return [table_node] + messages
|
||||
|
||||
def get_rows(self, table_data):
|
||||
rows = []
|
||||
groups = []
|
||||
trow = nodes.row()
|
||||
entry = nodes.entry()
|
||||
para = nodes.paragraph(text=unicode(table_data))
|
||||
entry += para
|
||||
trow += entry
|
||||
rows.append(trow)
|
||||
return rows, groups
|
||||
|
||||
# Add a column for a field. In order to have the RST inside
|
||||
# these fields get rendered, we need to use the
|
||||
# ViewList. Note, ViewList expects a list of lines, so chunk
|
||||
# up our content as a list to make it happy.
|
||||
def add_col(self, value):
|
||||
entry = nodes.entry()
|
||||
result = ViewList(value.split('\n'))
|
||||
self.state.nested_parse(result, 0, entry)
|
||||
return entry
|
||||
|
||||
def show_no_yaml_error(self):
|
||||
trow = nodes.row(classes=["no_yaml"])
|
||||
trow += self.add_col("No yaml found %s" % self.yaml_file)
|
||||
trow += self.add_col("")
|
||||
trow += self.add_col("")
|
||||
trow += self.add_col("")
|
||||
return trow
|
||||
|
||||
def collect_rows(self):
|
||||
rows = []
|
||||
groups = []
|
||||
try:
|
||||
# self.app.info("Parsed content is: %s" % self.yaml)
|
||||
for key, values in self.yaml:
|
||||
min_version = values.get('min_version', '')
|
||||
max_version = values.get('max_version', '')
|
||||
desc = values.get('description', '')
|
||||
classes = []
|
||||
if min_version:
|
||||
desc += ("\n\n**New in version %s**\n" % min_version)
|
||||
min_ver_css_name = ("rp_min_ver_" +
|
||||
str(min_version).replace('.', '_'))
|
||||
classes.append(min_ver_css_name)
|
||||
if max_version:
|
||||
desc += ("\n\n**Deprecated in version %s**\n" %
|
||||
max_version)
|
||||
max_ver_css_name = ("rp_max_ver_" +
|
||||
str(max_version).replace('.', '_'))
|
||||
classes.append(max_ver_css_name)
|
||||
trow = nodes.row(classes=classes)
|
||||
name = key
|
||||
if values.get('required', False) is False:
|
||||
name += " (Optional)"
|
||||
trow += self.add_col(name)
|
||||
trow += self.add_col(values.get('in'))
|
||||
trow += self.add_col(values.get('type'))
|
||||
trow += self.add_col(desc)
|
||||
rows.append(trow)
|
||||
except AttributeError as exc:
|
||||
if 'key' in locals():
|
||||
self.app.warn("Failure on key: %s, values: %s. %s" %
|
||||
(key, values, exc))
|
||||
else:
|
||||
rows.append(self.show_no_yaml_error())
|
||||
return rows, groups
|
||||
|
||||
def build_table(self):
|
||||
table = nodes.table()
|
||||
tgroup = nodes.tgroup(cols=len(self.headers))
|
||||
table += tgroup
|
||||
|
||||
# TODO(sdague): it would be really nice to figure out how not
|
||||
# to have this stanza, it kind of messes up all of the table
|
||||
# formatting because it doesn't let tables just be the right
|
||||
# size.
|
||||
tgroup.extend(
|
||||
nodes.colspec(colwidth=col_width, colname='c' + str(idx))
|
||||
for idx, col_width in enumerate(self.col_widths)
|
||||
)
|
||||
|
||||
thead = nodes.thead()
|
||||
tgroup += thead
|
||||
|
||||
row_node = nodes.row()
|
||||
thead += row_node
|
||||
row_node.extend(nodes.entry(h, nodes.paragraph(text=h))
|
||||
for h in self.headers)
|
||||
|
||||
tbody = nodes.tbody()
|
||||
tgroup += tbody
|
||||
|
||||
rows, groups = self.collect_rows()
|
||||
tbody.extend(rows)
|
||||
table.extend(groups)
|
||||
|
||||
return table
|
||||
|
||||
|
||||
def rest_method_html(self, node):
|
||||
tmpl = """
|
||||
<div class="operation-grp %(css_classes)s">
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<div class="operation">
|
||||
<a name="%(target)s" class="operation-anchor" href="#%(target)s"
|
||||
onclick="window.location.hash = hash;"
|
||||
>
|
||||
<span class="glyphicon glyphicon-link"></span></a>
|
||||
<span class="label label-%(method)s">%(method)s</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<div class="row">
|
||||
<div class="endpoint-container">
|
||||
<div class="row col-md-12">%(url)s</div>
|
||||
<div class="row col-md-12"><p class="url-subtitle">%(desc)s</p></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-1">
|
||||
<button
|
||||
class="btn btn-info btn-sm btn-detail"
|
||||
data-target="#%(target)s-detail"
|
||||
data-toggle="collapse"
|
||||
id="%(target)s-detail-btn"
|
||||
>detail</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>"""
|
||||
|
||||
node['url'] = node['url'].replace(
|
||||
'{',
|
||||
'<span class="path_parameter">{')
|
||||
node['url'] = node['url'].replace(
|
||||
'}',
|
||||
'}</span>')
|
||||
|
||||
self.body.append(tmpl % node)
|
||||
raise nodes.SkipNode
|
||||
|
||||
|
||||
def rest_expand_all_html(self, node):
|
||||
tmpl = """
|
||||
<div class="row">
|
||||
%(extra_js)s
|
||||
<div class="col-md-2 col-md-offset-9">
|
||||
%(selector)s
|
||||
</div>
|
||||
<div class=col-md-1>
|
||||
<button id="expand-all"
|
||||
data-toggle="collapse"
|
||||
class="btn btn-info btn-sm btn-expand-all"
|
||||
>Show All</button>
|
||||
</div>
|
||||
</div>"""
|
||||
|
||||
node.setdefault('selector', "")
|
||||
node.setdefault('extra_js', "")
|
||||
|
||||
if node['major']:
|
||||
node['selector'], node['extra_js'] = create_mv_selector(node)
|
||||
|
||||
self.body.append(tmpl % node)
|
||||
raise nodes.SkipNode
|
||||
|
||||
|
||||
def create_mv_selector(node):
|
||||
|
||||
mv_list = '<option value="" selected="selected">All</option>'
|
||||
|
||||
for x in range(node['min_ver'], node['max_ver'] + 1):
|
||||
mv_list += build_mv_item(node['major'], x, node['releases'])
|
||||
|
||||
selector_tmpl = """
|
||||
<form class=form-inline">
|
||||
<div class="form-group">
|
||||
<label class="control-label">
|
||||
Microversions
|
||||
</label>
|
||||
<select class="combobox form-control" id="mv_select">
|
||||
%(mv_list)s
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
"""
|
||||
|
||||
js_tmpl = """
|
||||
<script>
|
||||
os_max_mv = %(max)d;
|
||||
os_min_mv = %(min)d;
|
||||
</script>
|
||||
"""
|
||||
|
||||
selector_content = {
|
||||
'mv_list': mv_list
|
||||
}
|
||||
|
||||
js_content = {
|
||||
'min': node['min_ver'],
|
||||
'max': node['max_ver']
|
||||
}
|
||||
|
||||
return selector_tmpl % selector_content, js_tmpl % js_content
|
||||
|
||||
|
||||
def build_mv_item(major, micro, releases):
|
||||
version = "%d.%d" % (major, micro)
|
||||
if version in releases:
|
||||
return '<option value="%s">%s - %s</option>' % (
|
||||
version, version, releases[version].capitalize())
|
||||
else:
|
||||
return '<option value="%s">%s</option>' % (version, version)
|
||||
|
||||
|
||||
def resolve_rest_references(app, doctree):
|
||||
for node in doctree.traverse():
|
||||
if isinstance(node, rest_method):
|
||||
rest_node = node
|
||||
rest_method_section = node.parent
|
||||
rest_section = rest_method_section.parent
|
||||
gp = rest_section.parent
|
||||
|
||||
# Added required classes to the top section
|
||||
rest_section.attributes['classes'].append('api-detail')
|
||||
rest_section.attributes['classes'].append('collapse')
|
||||
|
||||
# Pop the title off the collapsed section
|
||||
title = rest_section.children.pop(0)
|
||||
rest_node['desc'] = title.children[0]
|
||||
|
||||
# In order to get the links in the sidebar to be right, we
|
||||
# have to do some id flipping here late in the game. The
|
||||
# rest_method_section has basically had a dummy id up
|
||||
# until this point just to keep it from colliding with
|
||||
# it's parent.
|
||||
rest_section.attributes['ids'][0] = (
|
||||
"%s-detail" % rest_section.attributes['ids'][0])
|
||||
rest_method_section.attributes['ids'][0] = rest_node['target']
|
||||
|
||||
# Pop the overall section into it's grand parent,
|
||||
# right before where the current parent lives
|
||||
idx = gp.children.index(rest_section)
|
||||
rest_section.remove(rest_method_section)
|
||||
gp.insert(idx, rest_method_section)
|
||||
|
||||
|
||||
def copy_assets(app, exception):
|
||||
assets = ('api-site.css', 'api-site.js', 'combobox.js')
|
||||
fonts = (
|
||||
'glyphicons-halflings-regular.ttf',
|
||||
'glyphicons-halflings-regular.woff'
|
||||
)
|
||||
if app.builder.name != 'html' or exception:
|
||||
return
|
||||
app.info('Copying assets: %s' % ', '.join(assets))
|
||||
app.info('Copying fonts: %s' % ', '.join(fonts))
|
||||
for asset in assets:
|
||||
dest = os.path.join(app.builder.outdir, '_static', asset)
|
||||
source = os.path.abspath(os.path.dirname(__file__))
|
||||
copyfile(os.path.join(source, 'assets', asset), dest)
|
||||
for font in fonts:
|
||||
dest = os.path.join(app.builder.outdir, '_static/fonts', font)
|
||||
source = os.path.abspath(os.path.dirname(__file__))
|
||||
copyfile(os.path.join(source, 'assets', font), dest)
|
||||
|
||||
|
||||
def add_assets(app):
|
||||
app.add_stylesheet('api-site.css')
|
||||
app.add_javascript('api-site.js')
|
||||
app.add_javascript('combobox.js')
|
||||
|
||||
|
||||
def setup(app):
|
||||
# Add some config options around microversions
|
||||
app.add_config_value('os_api_ref_max_microversion', '', 'env')
|
||||
app.add_config_value('os_api_ref_min_microversion', '', 'env')
|
||||
app.add_config_value('os_api_ref_release_microversions', '', 'env')
|
||||
# TODO(sdague): if someone wants to support latex/pdf, or man page
|
||||
# generation using these stanzas, here is where you'd need to
|
||||
# specify content specific renderers.
|
||||
app.add_node(rest_method,
|
||||
html=(rest_method_html, None))
|
||||
app.add_node(rest_expand_all,
|
||||
html=(rest_expand_all_html, None))
|
||||
app.add_node(http_code,
|
||||
html=(http_code_html, None))
|
||||
|
||||
# This specifies all our directives that we're adding
|
||||
app.add_directive('rest_parameters', RestParametersDirective)
|
||||
app.add_directive('rest_method', RestMethodDirective)
|
||||
app.add_directive('rest_expand_all', RestExpandAllDirective)
|
||||
app.add_directive('rest_status_code', HTTPResponseCodeDirective)
|
||||
|
||||
# The doctree-read hook is used do the slightly crazy doc
|
||||
# transformation that we do to get the rest_method document
|
||||
# structure.
|
||||
app.connect('doctree-read', resolve_rest_references)
|
||||
|
||||
# Add all the static assets to our build during the early stage of building
|
||||
app.connect('builder-inited', add_assets)
|
||||
|
||||
# This copies all the assets (css, js, fonts) over to the build
|
||||
# _static directory during final build.
|
||||
app.connect('build-finished', copy_assets)
|
||||
|
||||
return {'version': __version__}
|
|
@ -1,218 +0,0 @@
|
|||
tt.literal {
|
||||
padding: 2px 4px;
|
||||
font-size: 90%;
|
||||
color: #c7254e;
|
||||
white-space: nowrap;
|
||||
background-color: #f9f2f4;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* bootstrap users blockquote for pull quotes, so they are much
|
||||
larger, we need them smaller */
|
||||
blockquote { font-size: 1em; }
|
||||
|
||||
.docs-book-wrapper {
|
||||
width: 90% !important
|
||||
}
|
||||
|
||||
tbody>tr:nth-child(odd)>td,
|
||||
tbody>tr:nth-child(odd)>th {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
|
||||
td>p {
|
||||
margin: 0 0 0.5em;
|
||||
}
|
||||
|
||||
.operation-grp {
|
||||
padding-top: 0.5em;
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
|
||||
/* Ensure the method buttons and their links don't split lines when
|
||||
the page is narrower */
|
||||
.operation {
|
||||
/* this moves the link icon into the gutter */
|
||||
margin-left: -1.25em;
|
||||
margin-right: 1.25em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* These make the links only show up on hover */
|
||||
a.operation-anchor {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.operation-grp:hover a.operation-anchor {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/* All parameter tables should be full width */
|
||||
|
||||
.api-detail table.docutils {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.versionmodified {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.label-POST {
|
||||
background-color: #5cb85c;
|
||||
}
|
||||
.label-POST[href]:hover,
|
||||
.label-POST[href]:focus {
|
||||
background-color: #449d44;
|
||||
}
|
||||
.label-GET,
|
||||
.label-HEAD {
|
||||
background-color: #5bc0de;
|
||||
}
|
||||
.label-GET[href]:hover,
|
||||
.label-GET[href]:focus,
|
||||
.label-HEAD[href]:hover,
|
||||
.label-HEAD[href]:focus {
|
||||
background-color: #31b0d5;
|
||||
}
|
||||
.label-PUT,
|
||||
.label-PATCH {
|
||||
background-color: #f0ad4e;
|
||||
}
|
||||
.label-PUT[href]:hover,
|
||||
.label-PUT[href]:focus,
|
||||
.label-PATCH[href]:hover,
|
||||
.label-PATCH[href]:focus {
|
||||
background-color: #ec971f;
|
||||
}
|
||||
.label-COPY {
|
||||
background-color: #6666ff;
|
||||
}
|
||||
.label-COPY[href]:hover,
|
||||
.label-COPY[href]:focus {
|
||||
background-color: #6699ff;
|
||||
}
|
||||
.label-DELETE {
|
||||
background-color: #d9534f;
|
||||
}
|
||||
.label-DELETE[href]:hover,
|
||||
.label-DELETE[href]:focus {
|
||||
background-color: #c9302c;
|
||||
}
|
||||
|
||||
.btn-detail:hover, .btn-detail:focus,
|
||||
.btn-expand-all:hover, .btn-expand-all:focus {
|
||||
color: #fff;
|
||||
background-color: #3b6c91;
|
||||
border-color: #269abc;
|
||||
}
|
||||
|
||||
.btn-detail,
|
||||
.btn-expand-all {
|
||||
color: #fff;
|
||||
background-color: #2A4E68;
|
||||
}
|
||||
|
||||
span.path_parameter {
|
||||
font-family: monospace;
|
||||
padding: 2px 4px;
|
||||
font-size: 90%;
|
||||
color: #c7254e;
|
||||
white-space: nowrap;
|
||||
background-color: #f9f2f4;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* for microversion selector */
|
||||
.mv_selector {
|
||||
font-size: 0.8em;
|
||||
padding: 0.3em;
|
||||
}
|
||||
|
||||
.mv_selector.active {
|
||||
color: #fff;
|
||||
background-color: #31b0d5;
|
||||
border-color: #269abc;
|
||||
}
|
||||
|
||||
p.url-subtitle {
|
||||
color: #555;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.docs-body .section h1 {
|
||||
display: block;
|
||||
}
|
||||
|
||||
div.docs-sidebar-toc > div > ul > li {
|
||||
list-style-type: none;
|
||||
font-size: 0.8em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.docs-sidebar-toc > div > ul {
|
||||
padding-left: 20px
|
||||
}
|
||||
|
||||
div.docs-sidebar-toc > div > ul > li > ul > li {
|
||||
list-style-type: disc;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
div.docs-top-contents {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.endpoint-container{
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
#expand-all {
|
||||
margin-top: 23px;
|
||||
}
|
||||
|
||||
### Combobox Experiment
|
||||
@media (min-width: 768px) {
|
||||
.form-search .combobox-container,
|
||||
.form-inline .combobox-container {
|
||||
display: inline-block;
|
||||
margin-bottom: 0;
|
||||
vertical-align: top;
|
||||
}
|
||||
.form-search .combobox-container .input-group-addon,
|
||||
.form-inline .combobox-container .input-group-addon {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
.combobox-selected .caret {
|
||||
display: none;
|
||||
}
|
||||
/* :not doesn't work in IE8 */
|
||||
.combobox-container:not(.combobox-selected) .glyphicon-remove {
|
||||
display: none;
|
||||
}
|
||||
.typeahead-long {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.control-group.error .combobox-container .add-on {
|
||||
color: #B94A48;
|
||||
border-color: #B94A48;
|
||||
}
|
||||
.control-group.error .combobox-container .caret {
|
||||
border-top-color: #B94A48;
|
||||
}
|
||||
.control-group.warning .combobox-container .add-on {
|
||||
color: #C09853;
|
||||
border-color: #C09853;
|
||||
}
|
||||
.control-group.warning .combobox-container .caret {
|
||||
border-top-color: #C09853;
|
||||
}
|
||||
.control-group.success .combobox-container .add-on {
|
||||
color: #468847;
|
||||
border-color: #468847;
|
||||
}
|
||||
.control-group.success .combobox-container .caret {
|
||||
border-top-color: #468847;
|
||||
}
|
|
@ -1,178 +0,0 @@
|
|||
(function() {
|
||||
// the list of expanded element ids
|
||||
var expanded = [];
|
||||
// whether we should sync expand changes with the location
|
||||
// url. We need to make this false during large scale
|
||||
// operations because we're using the history API, which is
|
||||
// expensive. So a bulk expand turns this off, expands
|
||||
// everything, turns it back on, then does a history sync.
|
||||
var should_sync = true;
|
||||
|
||||
$(document).ready(function() {
|
||||
// Change the text on the expando buttons when
|
||||
// appropriate. This also add or removes them to the list of
|
||||
// expanded sections, and then syncs that list to the history
|
||||
// after such a change.
|
||||
$('.api-detail')
|
||||
.on('hide.bs.collapse', function(e) {
|
||||
processButton(this, 'detail');
|
||||
var index = expanded.indexOf(this.id);
|
||||
if (index > -1) {
|
||||
expanded.splice(index, 1);
|
||||
}
|
||||
sync_expanded();
|
||||
})
|
||||
.on('show.bs.collapse', function(e) {
|
||||
processButton(this, 'close');
|
||||
expanded.push(this.id);
|
||||
sync_expanded();
|
||||
});
|
||||
|
||||
// Expand the world. Wires up the expand all button, it turns
|
||||
// off the sync while it is running to save the costs with the
|
||||
// history API.
|
||||
var expandAllActive = true;
|
||||
$('#expand-all').click(function () {
|
||||
should_sync = false;
|
||||
if (expandAllActive) {
|
||||
expandAllActive = false;
|
||||
$('.api-detail').collapse('show');
|
||||
$('#expand-all').attr('data-toggle', '');
|
||||
$(this).text('Hide All');
|
||||
} else {
|
||||
expandAllActive = true;
|
||||
$('.api-detail').collapse('hide');
|
||||
$('#expand-all').attr('data-toggle', 'collapse');
|
||||
$(this).text('Show All');
|
||||
}
|
||||
should_sync = true;
|
||||
sync_expanded();
|
||||
});
|
||||
|
||||
// if there is an expanded parameter passed in a url, we run
|
||||
// through and expand all the appropriate things.
|
||||
if (window.location.search.substring(1).indexOf("expanded") > -1) {
|
||||
should_sync = false;
|
||||
var parts = window.location.search.substring(1).split('&');
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
var keyval = parts[i].split('=');
|
||||
if (keyval[0] == "expanded" && keyval[1]) {
|
||||
var expanded_ids = keyval[1].split(',');
|
||||
for (var j = 0; j < expanded_ids.length; j++) {
|
||||
$('#' + expanded_ids[j]).collapse('show');
|
||||
}
|
||||
}
|
||||
}
|
||||
should_sync = true;
|
||||
// This is needed because the hash *might* be inside a
|
||||
// collapsed section.
|
||||
//
|
||||
// NOTE(sdague): this doesn't quite seem to work while
|
||||
// we're changing the rest of the document.
|
||||
$(document.body).scrollTop($(window.location.hash).offset().top);
|
||||
}
|
||||
|
||||
// Wire up microversion selector
|
||||
$('.mv_selector').on('click', function(e) {
|
||||
var version = e.currentTarget.innerHTML;
|
||||
// flip what is active
|
||||
$(this).addClass('active').siblings().removeClass('active');
|
||||
if (version == "All") {
|
||||
reset_microversion();
|
||||
} else {
|
||||
set_microversion(version);
|
||||
}
|
||||
});
|
||||
});
|
||||
/**
|
||||
* Helper function for setting the text, styles for expandos
|
||||
*/
|
||||
function processButton(button, text) {
|
||||
$('#' + $(button).attr('id') + '-btn').text(text)
|
||||
.toggleClass('btn-info')
|
||||
.toggleClass('btn-default');
|
||||
}
|
||||
|
||||
// Take the expanded array and push it into history. Because
|
||||
// sphinx is building css appropriate ids, they should not have
|
||||
// any special characters we need to encode. So we can simply join
|
||||
// them into a comma separated list.
|
||||
function sync_expanded() {
|
||||
if (should_sync) {
|
||||
var url = UpdateQueryString('expanded', expanded.join(','));
|
||||
history.pushState('', 'new expand', url);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Generically update the query string for a url. Credit to
|
||||
// http://stackoverflow.com/questions/5999118/add-or-update-query-string-parameter
|
||||
// for making this properly generic.
|
||||
function UpdateQueryString(key, value, url) {
|
||||
if (!url) url = window.location.href;
|
||||
var re = new RegExp("([?&])" + key + "=.*?(&|#|$)(.*)", "gi"),
|
||||
hash;
|
||||
|
||||
if (re.test(url)) {
|
||||
if (typeof value !== 'undefined' && value !== null)
|
||||
return url.replace(re, '$1' + key + "=" + value + '$2$3');
|
||||
else {
|
||||
hash = url.split('#');
|
||||
url = hash[0].replace(re, '$1$3').replace(/(&|\?)$/, '');
|
||||
if (typeof hash[1] !== 'undefined' && hash[1] !== null)
|
||||
url += '#' + hash[1];
|
||||
return url;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (typeof value !== 'undefined' && value !== null) {
|
||||
var separator = url.indexOf('?') !== -1 ? '&' : '?';
|
||||
hash = url.split('#');
|
||||
url = hash[0] + separator + key + '=' + value;
|
||||
if (typeof hash[1] !== 'undefined' && hash[1] !== null)
|
||||
url += '#' + hash[1];
|
||||
return url;
|
||||
}
|
||||
else
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the Y value of the microversion to turn on / off visibility
|
||||
// of components.
|
||||
function set_microversion(number) {
|
||||
var major = number.split(".")[0];
|
||||
var micro = number.split(".")[1];
|
||||
for (var i = os_min_mv; i <= os_max_mv; i++) {
|
||||
var max_class = ".rp_max_ver_" + major + "_" + i;
|
||||
var min_class = ".rp_min_ver_" + major + "_" + i;
|
||||
if (i < micro) {
|
||||
$(max_class).hide(400);
|
||||
$(min_class).show(400);
|
||||
} else if (i >= micro) {
|
||||
$(min_class).hide(400);
|
||||
$(max_class).show(400);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function reset_microversion() {
|
||||
$('[class^=rp_min_ver]').show(400);
|
||||
$('[class^=rp_max_ver]').show(400);
|
||||
}
|
||||
|
||||
|
||||
$(document).ready(function(){
|
||||
$('#mv_select').combobox({appendId: '-visable'});
|
||||
$('#mv_select').on('change', function() {
|
||||
var version = this.value;
|
||||
if (version == "") {
|
||||
reset_microversion();
|
||||
} else {
|
||||
set_microversion(version);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
})();
|
|
@ -1,462 +0,0 @@
|
|||
/* =============================================================
|
||||
* bootstrap-combobox.js v1.1.7
|
||||
* =============================================================
|
||||
* Copyright 2012 Daniel Farrell
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* ============================================================ */
|
||||
|
||||
!function( $ ) {
|
||||
|
||||
"use strict";
|
||||
|
||||
/* COMBOBOX PUBLIC CLASS DEFINITION
|
||||
* ================================ */
|
||||
|
||||
var Combobox = function ( element, options ) {
|
||||
this.options = $.extend({}, $.fn.combobox.defaults, options);
|
||||
this.template = this.options.template || this.template
|
||||
this.$source = $(element);
|
||||
this.$container = this.setup();
|
||||
this.$element = this.$container.find('input[type=text]');
|
||||
this.$target = this.$container.find('input[type=hidden]');
|
||||
this.$button = this.$container.find('.dropdown-toggle');
|
||||
this.$menu = $(this.options.menu).appendTo('body');
|
||||
this.matcher = this.options.matcher || this.matcher;
|
||||
this.sorter = this.options.sorter || this.sorter;
|
||||
this.highlighter = this.options.highlighter || this.highlighter;
|
||||
this.shown = false;
|
||||
this.selected = false;
|
||||
this.refresh();
|
||||
this.transferAttributes();
|
||||
this.listen();
|
||||
};
|
||||
|
||||
Combobox.prototype = {
|
||||
|
||||
constructor: Combobox
|
||||
|
||||
, setup: function () {
|
||||
var combobox = $(this.template());
|
||||
this.$source.before(combobox);
|
||||
this.$source.hide();
|
||||
return combobox;
|
||||
}
|
||||
|
||||
, disable: function() {
|
||||
this.$element.prop('disabled', true);
|
||||
this.$button.attr('disabled', true);
|
||||
this.disabled = true;
|
||||
this.$container.addClass('combobox-disabled');
|
||||
}
|
||||
|
||||
, enable: function() {
|
||||
this.$element.prop('disabled', false);
|
||||
this.$button.attr('disabled', false);
|
||||
this.disabled = false;
|
||||
this.$container.removeClass('combobox-disabled');
|
||||
}
|
||||
, parse: function () {
|
||||
var that = this
|
||||
, map = {}
|
||||
, source = []
|
||||
, selected = false
|
||||
, selectedValue = '';
|
||||
this.$source.find('option').each(function() {
|
||||
var option = $(this);
|
||||
if (option.val() === '') {
|
||||
that.options.placeholder = option.text();
|
||||
return;
|
||||
}
|
||||
map[option.text()] = option.val();
|
||||
source.push(option.text());
|
||||
if (option.prop('selected')) {
|
||||
selected = option.text();
|
||||
selectedValue = option.val();
|
||||
}
|
||||
})
|
||||
this.map = map;
|
||||
if (selected) {
|
||||
this.$element.val(selected);
|
||||
this.$target.val(selectedValue);
|
||||
this.$container.addClass('combobox-selected');
|
||||
this.selected = true;
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
, transferAttributes: function() {
|
||||
this.options.placeholder = this.$source.attr('data-placeholder') || this.options.placeholder
|
||||
if(this.options.appendId !== "undefined") {
|
||||
this.$element.attr('id', this.$source.attr('id') + this.options.appendId);
|
||||
}
|
||||
this.$element.attr('placeholder', this.options.placeholder)
|
||||
this.$target.prop('name', this.$source.prop('name'))
|
||||
this.$target.val(this.$source.val())
|
||||
this.$source.removeAttr('name') // Remove from source otherwise form will pass parameter twice.
|
||||
this.$element.attr('required', this.$source.attr('required'))
|
||||
this.$element.attr('rel', this.$source.attr('rel'))
|
||||
this.$element.attr('title', this.$source.attr('title'))
|
||||
this.$element.attr('class', this.$source.attr('class'))
|
||||
this.$element.attr('tabindex', this.$source.attr('tabindex'))
|
||||
this.$source.removeAttr('tabindex')
|
||||
if (this.$source.attr('disabled')!==undefined)
|
||||
this.disable();
|
||||
}
|
||||
|
||||
, select: function () {
|
||||
var val = this.$menu.find('.active').attr('data-value');
|
||||
this.$element.val(this.updater(val)).trigger('change');
|
||||
this.$target.val(this.map[val]).trigger('change');
|
||||
this.$source.val(this.map[val]).trigger('change');
|
||||
this.$container.addClass('combobox-selected');
|
||||
this.selected = true;
|
||||
return this.hide();
|
||||
}
|
||||
|
||||
, updater: function (item) {
|
||||
return item;
|
||||
}
|
||||
|
||||
, show: function () {
|
||||
var pos = $.extend({}, this.$element.position(), {
|
||||
height: this.$element[0].offsetHeight
|
||||
});
|
||||
|
||||
this.$menu
|
||||
.insertAfter(this.$element)
|
||||
.css({
|
||||
top: pos.top + pos.height
|
||||
, left: pos.left
|
||||
})
|
||||
.show();
|
||||
|
||||
$('.dropdown-menu').on('mousedown', $.proxy(this.scrollSafety, this));
|
||||
|
||||
this.shown = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
, hide: function () {
|
||||
this.$menu.hide();
|
||||
$('.dropdown-menu').off('mousedown', $.proxy(this.scrollSafety, this));
|
||||
this.$element.on('blur', $.proxy(this.blur, this));
|
||||
this.shown = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
, lookup: function (event) {
|
||||
this.query = this.$element.val();
|
||||
return this.process(this.source);
|
||||
}
|
||||
|
||||
, process: function (items) {
|
||||
var that = this;
|
||||
|
||||
items = $.grep(items, function (item) {
|
||||
return that.matcher(item);
|
||||
})
|
||||
|
||||
items = this.sorter(items);
|
||||
|
||||
if (!items.length) {
|
||||
return this.shown ? this.hide() : this;
|
||||
}
|
||||
|
||||
return this.render(items.slice(0, this.options.items)).show();
|
||||
}
|
||||
|
||||
, template: function() {
|
||||
if (this.options.bsVersion == '2') {
|
||||
return '<div class="combobox-container"><input type="hidden" /> <div class="input-append"> <input type="text" autocomplete="false" /> <span class="add-on dropdown-toggle" data-dropdown="dropdown"> <span class="caret"/> <i class="icon-remove"/> </span> </div> </div>'
|
||||
} else {
|
||||
return '<div class="combobox-container"> <input type="hidden" /> <div class="input-group"> <input type="text" autocomplete="false" /> <span class="input-group-addon dropdown-toggle" data-dropdown="dropdown"> <span class="caret" /> <span class="glyphicon glyphicon-remove" /> </span> </div> </div>'
|
||||
}
|
||||
}
|
||||
|
||||
, matcher: function (item) {
|
||||
return ~item.toLowerCase().indexOf(this.query.toLowerCase());
|
||||
}
|
||||
|
||||
, sorter: function (items) {
|
||||
var beginswith = []
|
||||
, caseSensitive = []
|
||||
, caseInsensitive = []
|
||||
, item;
|
||||
|
||||
while (item = items.shift()) {
|
||||
if (!item.toLowerCase().indexOf(this.query.toLowerCase())) {beginswith.push(item);}
|
||||
else if (~item.indexOf(this.query)) {caseSensitive.push(item);}
|
||||
else {caseInsensitive.push(item);}
|
||||
}
|
||||
|
||||
return beginswith.concat(caseSensitive, caseInsensitive);
|
||||
}
|
||||
|
||||
, highlighter: function (item) {
|
||||
var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&');
|
||||
return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) {
|
||||
return '<strong>' + match + '</strong>';
|
||||
})
|
||||
}
|
||||
|
||||
, render: function (items) {
|
||||
var that = this;
|
||||
|
||||
items = $(items).map(function (i, item) {
|
||||
i = $(that.options.item).attr('data-value', item);
|
||||
i.find('a').html(that.highlighter(item));
|
||||
return i[0];
|
||||
})
|
||||
|
||||
items.first().addClass('active');
|
||||
this.$menu.html(items);
|
||||
return this;
|
||||
}
|
||||
|
||||
, next: function (event) {
|
||||
var active = this.$menu.find('.active').removeClass('active')
|
||||
, next = active.next();
|
||||
|
||||
if (!next.length) {
|
||||
next = $(this.$menu.find('li')[0]);
|
||||
}
|
||||
|
||||
next.addClass('active');
|
||||
}
|
||||
|
||||
, prev: function (event) {
|
||||
var active = this.$menu.find('.active').removeClass('active')
|
||||
, prev = active.prev();
|
||||
|
||||
if (!prev.length) {
|
||||
prev = this.$menu.find('li').last();
|
||||
}
|
||||
|
||||
prev.addClass('active');
|
||||
}
|
||||
|
||||
, toggle: function () {
|
||||
if (!this.disabled) {
|
||||
if (this.$container.hasClass('combobox-selected')) {
|
||||
this.clearTarget();
|
||||
this.triggerChange();
|
||||
this.clearElement();
|
||||
} else {
|
||||
if (this.shown) {
|
||||
this.hide();
|
||||
} else {
|
||||
this.clearElement();
|
||||
this.lookup();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
, scrollSafety: function(e) {
|
||||
if (e.target.tagName == 'UL') {
|
||||
this.$element.off('blur');
|
||||
}
|
||||
}
|
||||
, clearElement: function () {
|
||||
this.$element.val('').focus();
|
||||
}
|
||||
|
||||
, clearTarget: function () {
|
||||
this.$source.val('');
|
||||
this.$target.val('');
|
||||
this.$container.removeClass('combobox-selected');
|
||||
this.selected = false;
|
||||
}
|
||||
|
||||
, triggerChange: function () {
|
||||
this.$source.trigger('change');
|
||||
}
|
||||
|
||||
, refresh: function () {
|
||||
this.source = this.parse();
|
||||
this.options.items = this.source.length;
|
||||
}
|
||||
|
||||
, listen: function () {
|
||||
this.$element
|
||||
.on('focus', $.proxy(this.focus, this))
|
||||
.on('blur', $.proxy(this.blur, this))
|
||||
.on('keypress', $.proxy(this.keypress, this))
|
||||
.on('keyup', $.proxy(this.keyup, this));
|
||||
|
||||
if (this.eventSupported('keydown')) {
|
||||
this.$element.on('keydown', $.proxy(this.keydown, this));
|
||||
}
|
||||
|
||||
this.$menu
|
||||
.on('click', $.proxy(this.click, this))
|
||||
.on('mouseenter', 'li', $.proxy(this.mouseenter, this))
|
||||
.on('mouseleave', 'li', $.proxy(this.mouseleave, this));
|
||||
|
||||
this.$button
|
||||
.on('click', $.proxy(this.toggle, this));
|
||||
}
|
||||
|
||||
, eventSupported: function(eventName) {
|
||||
var isSupported = eventName in this.$element;
|
||||
if (!isSupported) {
|
||||
this.$element.setAttribute(eventName, 'return;');
|
||||
isSupported = typeof this.$element[eventName] === 'function';
|
||||
}
|
||||
return isSupported;
|
||||
}
|
||||
|
||||
, move: function (e) {
|
||||
if (!this.shown) {return;}
|
||||
|
||||
switch(e.keyCode) {
|
||||
case 9: // tab
|
||||
case 13: // enter
|
||||
case 27: // escape
|
||||
e.preventDefault();
|
||||
break;
|
||||
|
||||
case 38: // up arrow
|
||||
e.preventDefault();
|
||||
this.prev();
|
||||
this.fixMenuScroll();
|
||||
break;
|
||||
|
||||
case 40: // down arrow
|
||||
e.preventDefault();
|
||||
this.next();
|
||||
this.fixMenuScroll();
|
||||
break;
|
||||
}
|
||||
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
, fixMenuScroll: function(){
|
||||
var active = this.$menu.find('.active');
|
||||
if(active.length){
|
||||
var top = active.position().top;
|
||||
var bottom = top + active.height();
|
||||
var scrollTop = this.$menu.scrollTop();
|
||||
var menuHeight = this.$menu.height();
|
||||
if(bottom > menuHeight){
|
||||
this.$menu.scrollTop(scrollTop + bottom - menuHeight);
|
||||
} else if(top < 0){
|
||||
this.$menu.scrollTop(scrollTop + top);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
, keydown: function (e) {
|
||||
this.suppressKeyPressRepeat = ~$.inArray(e.keyCode, [40,38,9,13,27]);
|
||||
this.move(e);
|
||||
}
|
||||
|
||||
, keypress: function (e) {
|
||||
if (this.suppressKeyPressRepeat) {return;}
|
||||
this.move(e);
|
||||
}
|
||||
|
||||
, keyup: function (e) {
|
||||
switch(e.keyCode) {
|
||||
case 40: // down arrow
|
||||
if (!this.shown){
|
||||
this.toggle();
|
||||
}
|
||||
break;
|
||||
case 39: // right arrow
|
||||
case 38: // up arrow
|
||||
case 37: // left arrow
|
||||
case 36: // home
|
||||
case 35: // end
|
||||
case 16: // shift
|
||||
case 17: // ctrl
|
||||
case 18: // alt
|
||||
break;
|
||||
|
||||
case 9: // tab
|
||||
case 13: // enter
|
||||
if (!this.shown) {return;}
|
||||
this.select();
|
||||
break;
|
||||
|
||||
case 27: // escape
|
||||
if (!this.shown) {return;}
|
||||
this.hide();
|
||||
break;
|
||||
|
||||
default:
|
||||
this.clearTarget();
|
||||
this.lookup();
|
||||
}
|
||||
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
, focus: function (e) {
|
||||
this.focused = true;
|
||||
}
|
||||
|
||||
, blur: function (e) {
|
||||
var that = this;
|
||||
this.focused = false;
|
||||
var val = this.$element.val();
|
||||
if (!this.selected && val !== '' ) {
|
||||
this.$element.val('');
|
||||
this.$source.val('').trigger('change');
|
||||
this.$target.val('').trigger('change');
|
||||
}
|
||||
if (!this.mousedover && this.shown) {setTimeout(function () { that.hide(); }, 200);}
|
||||
}
|
||||
|
||||
, click: function (e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
this.select();
|
||||
this.$element.focus();
|
||||
}
|
||||
|
||||
, mouseenter: function (e) {
|
||||
this.mousedover = true;
|
||||
this.$menu.find('.active').removeClass('active');
|
||||
$(e.currentTarget).addClass('active');
|
||||
}
|
||||
|
||||
, mouseleave: function (e) {
|
||||
this.mousedover = false;
|
||||
}
|
||||
};
|
||||
|
||||
/* COMBOBOX PLUGIN DEFINITION
|
||||
* =========================== */
|
||||
$.fn.combobox = function ( option ) {
|
||||
return this.each(function () {
|
||||
var $this = $(this)
|
||||
, data = $this.data('combobox')
|
||||
, options = typeof option == 'object' && option;
|
||||
if(!data) {$this.data('combobox', (data = new Combobox(this, options)));}
|
||||
if (typeof option == 'string') {data[option]();}
|
||||
});
|
||||
};
|
||||
|
||||
$.fn.combobox.defaults = {
|
||||
bsVersion: '3'
|
||||
, menu: '<ul class="typeahead typeahead-long dropdown-menu"></ul>'
|
||||
, item: '<li><a href="#"></a></li>'
|
||||
};
|
||||
|
||||
$.fn.combobox.Constructor = Combobox;
|
||||
|
||||
}( window.jQuery );
|
Binary file not shown.
Binary file not shown.
|
@ -1,237 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.parsers.rst.directives.tables import Table
|
||||
from docutils.statemachine import ViewList
|
||||
from six.moves.http_client import responses
|
||||
import yaml
|
||||
|
||||
# cache for file -> yaml so we only do the load and check of a yaml
|
||||
# file once during a sphinx processing run.
|
||||
HTTP_YAML_CACHE = {}
|
||||
|
||||
|
||||
class HTTPResponseCodeDirective(Table):
|
||||
|
||||
headers = ["Code", "Reason"]
|
||||
|
||||
status_types = ("success", "error")
|
||||
|
||||
# This is for HTTP response codes that OpenStack may use that are not part
|
||||
# the httplib response dict.
|
||||
CODES = {
|
||||
429: "Too Many Requests",
|
||||
}
|
||||
|
||||
required_arguments = 2
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.CODES.update(responses)
|
||||
super(HTTPResponseCodeDirective, self).__init__(*args, **kwargs)
|
||||
|
||||
def _load_status_file(self, fpath):
|
||||
global HTTP_YAML_CACHE
|
||||
if fpath in HTTP_YAML_CACHE:
|
||||
return HTTP_YAML_CACHE[fpath]
|
||||
|
||||
# self.app.info("Fpath: %s" % fpath)
|
||||
try:
|
||||
with open(fpath, 'r') as stream:
|
||||
lookup = yaml.safe_load(stream)
|
||||
except IOError:
|
||||
self.app.warn(
|
||||
"Parameters file %s not found" % fpath,
|
||||
(self.env.docname, None))
|
||||
return
|
||||
except yaml.YAMLError as exc:
|
||||
self.app.warn(exc)
|
||||
raise
|
||||
|
||||
HTTP_YAML_CACHE[fpath] = lookup
|
||||
return lookup
|
||||
|
||||
def run(self):
|
||||
self.env = self.state.document.settings.env
|
||||
self.app = self.env.app
|
||||
|
||||
# Make sure we have some content, which should be yaml that
|
||||
# defines some parameters.
|
||||
if not self.content:
|
||||
error = self.state_machine.reporter.error(
|
||||
'No parameters defined',
|
||||
nodes.literal_block(self.block_text, self.block_text),
|
||||
line=self.lineno)
|
||||
return [error]
|
||||
|
||||
if not len(self.arguments) >= 2:
|
||||
error = self.state_machine.reporter.error(
|
||||
'%s' % self.arguments,
|
||||
nodes.literal_block(self.block_text, self.block_text),
|
||||
line=self.lineno)
|
||||
return [error]
|
||||
|
||||
_, status_defs_file = self.env.relfn2path(self.arguments.pop())
|
||||
status_type = self.arguments.pop()
|
||||
|
||||
self.status_defs = self._load_status_file(status_defs_file)
|
||||
|
||||
# self.app.info("%s" % str(self.status_defs))
|
||||
|
||||
if status_type not in self.status_types:
|
||||
error = self.state_machine.reporter.error(
|
||||
'Type %s is not one of %s' % (status_type, self.status_types),
|
||||
nodes.literal_block(self.block_text, self.block_text),
|
||||
line=self.lineno)
|
||||
return [error]
|
||||
|
||||
self.yaml = self._load_codes()
|
||||
|
||||
self.max_cols = len(self.headers)
|
||||
# TODO(sdague): it would be good to dynamically set column
|
||||
# widths (or basically make the colwidth thing go away
|
||||
# entirely)
|
||||
self.options['widths'] = [30, 70]
|
||||
self.col_widths = self.get_column_widths(self.max_cols)
|
||||
if isinstance(self.col_widths, tuple):
|
||||
# In docutils 0.13.1, get_column_widths returns a (widths,
|
||||
# colwidths) tuple, where widths is a string (i.e. 'auto').
|
||||
# See https://sourceforge.net/p/docutils/patches/120/.
|
||||
self.col_widths = self.col_widths[1]
|
||||
# Actually convert the yaml
|
||||
title, messages = self.make_title()
|
||||
# self.app.info("Title %s, messages %s" % (title, messages))
|
||||
table_node = self.build_table()
|
||||
self.add_name(table_node)
|
||||
|
||||
title_block = nodes.title(
|
||||
text=status_type.capitalize())
|
||||
|
||||
section = nodes.section(ids=title_block)
|
||||
section += title_block
|
||||
section += table_node
|
||||
|
||||
return [section] + messages
|
||||
|
||||
def _load_codes(self):
|
||||
content = "\n".join(self.content)
|
||||
parsed = yaml.safe_load(content)
|
||||
|
||||
new_content = list()
|
||||
|
||||
for item in parsed:
|
||||
if isinstance(item, int):
|
||||
new_content.append((item, self.status_defs[item]['default']))
|
||||
else:
|
||||
try:
|
||||
for code, reason in item.items():
|
||||
new_content.append(
|
||||
(code, self.status_defs[code][reason])
|
||||
)
|
||||
except KeyError:
|
||||
self.app.warn(
|
||||
"Could not find %s for code %s" % (reason, code))
|
||||
new_content.append(
|
||||
(code, self.status_defs[code]['default']))
|
||||
|
||||
return new_content
|
||||
|
||||
def build_table(self):
|
||||
table = nodes.table()
|
||||
tgroup = nodes.tgroup(cols=len(self.headers))
|
||||
table += tgroup
|
||||
|
||||
# TODO(sdague): it would be really nice to figure out how not
|
||||
# to have this stanza, it kind of messes up all of the table
|
||||
# formatting because it doesn't let tables just be the right
|
||||
# size.
|
||||
tgroup.extend(
|
||||
nodes.colspec(colwidth=col_width, colname='c' + str(idx))
|
||||
for idx, col_width in enumerate(self.col_widths)
|
||||
)
|
||||
|
||||
thead = nodes.thead()
|
||||
tgroup += thead
|
||||
|
||||
row_node = nodes.row()
|
||||
thead += row_node
|
||||
row_node.extend(nodes.entry(h, nodes.paragraph(text=h))
|
||||
for h in self.headers)
|
||||
|
||||
tbody = nodes.tbody()
|
||||
tgroup += tbody
|
||||
|
||||
rows, groups = self.collect_rows()
|
||||
tbody.extend(rows)
|
||||
table.extend(groups)
|
||||
|
||||
return table
|
||||
|
||||
def add_col(self, node):
|
||||
entry = nodes.entry()
|
||||
entry.append(node)
|
||||
return entry
|
||||
|
||||
def add_desc_col(self, value):
|
||||
entry = nodes.entry()
|
||||
result = ViewList(value.split('\n'))
|
||||
self.state.nested_parse(result, 0, entry)
|
||||
return entry
|
||||
|
||||
def collect_rows(self):
|
||||
rows = []
|
||||
groups = []
|
||||
try:
|
||||
# self.app.info("Parsed content is: %s" % self.yaml)
|
||||
for code, desc in self.yaml:
|
||||
|
||||
h_code = http_code()
|
||||
h_code['code'] = code
|
||||
h_code['title'] = self.CODES.get(code, 'Unknown')
|
||||
|
||||
trow = nodes.row()
|
||||
trow += self.add_col(h_code)
|
||||
trow += self.add_desc_col(desc)
|
||||
rows.append(trow)
|
||||
except AttributeError as exc:
|
||||
# if 'key' in locals():
|
||||
self.app.warn("Failure on key: %s, values: %s. %s" %
|
||||
(code, desc, exc))
|
||||
# else:
|
||||
# rows.append(self.show_no_yaml_error())
|
||||
return rows, groups
|
||||
|
||||
|
||||
def http_code_html(self, node):
|
||||
tmpl = "<code>%(code)s - %(title)s</code>"
|
||||
self.body.append(tmpl % node)
|
||||
raise nodes.SkipNode
|
||||
|
||||
|
||||
class http_code(nodes.Part, nodes.Element):
|
||||
"""Node for http_code stanza
|
||||
|
||||
Because we need to insert very specific HTML at the final stage of
|
||||
processing, the http_code stanza needs a custom node type. This
|
||||
lets us accumulate the relevant data into this node, during
|
||||
parsing, but not turn it into known sphinx types (lists, tables,
|
||||
sections).
|
||||
|
||||
Then, during the final build phase we transform directly to the
|
||||
html that we want.
|
||||
|
||||
NOTE: this means we error trying to build latex or man pages for
|
||||
these stanza types right now. This is all fixable if we add an
|
||||
output formatter for this node type, but it's not yet a
|
||||
priority. Contributions welcomed.
|
||||
"""
|
||||
pass
|
|
@ -1,95 +0,0 @@
|
|||
# Copyright 2010-2011 OpenStack Foundation
|
||||
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import os
|
||||
|
||||
import fixtures
|
||||
import testtools
|
||||
|
||||
|
||||
def example_dir(name=""):
|
||||
return os.path.join(os.path.dirname(__file__), 'examples', name)
|
||||
|
||||
|
||||
_TRUE_VALUES = ('True', 'true', '1', 'yes')
|
||||
|
||||
|
||||
class OutputStreamCapture(fixtures.Fixture):
|
||||
"""Capture output streams during tests.
|
||||
|
||||
This fixture captures errant printing to stderr / stdout during
|
||||
the tests and lets us see those streams at the end of the test
|
||||
runs instead. Useful to see what was happening during failed
|
||||
tests.
|
||||
"""
|
||||
def setUp(self):
|
||||
super(OutputStreamCapture, self).setUp()
|
||||
if os.environ.get('OS_STDOUT_CAPTURE') in _TRUE_VALUES:
|
||||
self.out = self.useFixture(fixtures.StringStream('stdout'))
|
||||
self.useFixture(
|
||||
fixtures.MonkeyPatch('sys.stdout', self.out.stream))
|
||||
if os.environ.get('OS_STDERR_CAPTURE') in _TRUE_VALUES:
|
||||
self.err = self.useFixture(fixtures.StringStream('stderr'))
|
||||
self.useFixture(
|
||||
fixtures.MonkeyPatch('sys.stderr', self.err.stream))
|
||||
|
||||
@property
|
||||
def stderr(self):
|
||||
return self.err._details["stderr"].as_text()
|
||||
|
||||
@property
|
||||
def stdout(self):
|
||||
return self.out._details["stdout"].as_text()
|
||||
|
||||
|
||||
class Timeout(fixtures.Fixture):
|
||||
"""Setup per test timeouts.
|
||||
|
||||
In order to avoid test deadlocks we support setting up a test
|
||||
timeout parameter read from the environment. In almost all
|
||||
cases where the timeout is reached this means a deadlock.
|
||||
|
||||
A class level TIMEOUT_SCALING_FACTOR also exists, which allows
|
||||
extremely long tests to specify they need more time.
|
||||
"""
|
||||
|
||||
def __init__(self, timeout, scaling=1):
|
||||
super(Timeout, self).__init__()
|
||||
try:
|
||||
self.test_timeout = int(timeout)
|
||||
except ValueError:
|
||||
# If timeout value is invalid do not set a timeout.
|
||||
self.test_timeout = 0
|
||||
if scaling >= 1:
|
||||
self.test_timeout *= scaling
|
||||
else:
|
||||
raise ValueError('scaling value must be >= 1')
|
||||
|
||||
def setUp(self):
|
||||
super(Timeout, self).setUp()
|
||||
if self.test_timeout > 0:
|
||||
self.useFixture(fixtures.Timeout(self.test_timeout, gentle=True))
|
||||
|
||||
|
||||
class TestCase(testtools.TestCase):
|
||||
|
||||
"""Test case base class for all unit tests."""
|
||||
|
||||
def setUp(self):
|
||||
"""Run before each test method to initialize test environment."""
|
||||
super(TestCase, self).setUp()
|
||||
self.useFixture(Timeout(
|
||||
os.environ.get('OS_TEST_TIMEOUT', 0)))
|
||||
self.useFixture(OutputStreamCapture())
|
|
@ -1,35 +0,0 @@
|
|||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
# -- General configuration ----------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
|
||||
import openstackdocstheme
|
||||
|
||||
html_theme = 'openstackdocs'
|
||||
html_theme_path = [openstackdocstheme.get_html_theme_path()]
|
||||
html_theme_options = {
|
||||
"sidebar_mode": "toc",
|
||||
}
|
||||
|
||||
extensions = [
|
||||
'os_api_ref',
|
||||
]
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
|
@ -1,32 +0,0 @@
|
|||
.. rest_expand_all::
|
||||
|
||||
I am text, hear me roar!
|
||||
|
||||
==============
|
||||
List Servers
|
||||
==============
|
||||
|
||||
.. rest_method:: GET /servers
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- name: name
|
||||
|
||||
Response codes
|
||||
--------------
|
||||
|
||||
.. rest_status_code:: success status.yaml
|
||||
|
||||
- 200
|
||||
- 100
|
||||
- 201
|
||||
|
||||
|
||||
.. rest_status_code:: error status.yaml
|
||||
|
||||
- 405
|
||||
- 403
|
||||
- 401
|
||||
- 400
|
||||
- 500
|
||||
- 409: duplcate_zone
|
|
@ -1,6 +0,0 @@
|
|||
name:
|
||||
in: body
|
||||
required: true
|
||||
type: string
|
||||
description: |
|
||||
The name of things
|
|
@ -1,39 +0,0 @@
|
|||
#################
|
||||
# Success Codes #
|
||||
#################
|
||||
100:
|
||||
default: |
|
||||
An unusual code for an API
|
||||
200:
|
||||
default: |
|
||||
Request was successful.
|
||||
201:
|
||||
default: |
|
||||
Resource was created and is ready to use.
|
||||
|
||||
#################
|
||||
# Error Codes #
|
||||
#################
|
||||
|
||||
400:
|
||||
default: |
|
||||
Some content in the request was invalid
|
||||
zone_data_error: |
|
||||
Some of the data for the
|
||||
401:
|
||||
default: |
|
||||
User must authenticate before making a request
|
||||
403:
|
||||
default: |
|
||||
Policy does not allow current user to do this operation.
|
||||
405:
|
||||
default: |
|
||||
Method is not valid for this endpoint.
|
||||
409:
|
||||
default: |
|
||||
This operation conflicted with another operation on this resource
|
||||
duplcate_zone: |
|
||||
There is already a zone with this name.
|
||||
500:
|
||||
default: |
|
||||
Something went wrong inside the service.
|
|
@ -1,38 +0,0 @@
|
|||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
# -- General configuration ----------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
|
||||
import openstackdocstheme
|
||||
|
||||
html_theme = 'openstackdocs'
|
||||
html_theme_path = [openstackdocstheme.get_html_theme_path()]
|
||||
html_theme_options = {
|
||||
"sidebar_mode": "toc",
|
||||
}
|
||||
|
||||
extensions = [
|
||||
'os_api_ref',
|
||||
]
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
os_api_ref_min_microversion = '2.1'
|
||||
os_api_ref_max_microversion = '2.30'
|
|
@ -1,23 +0,0 @@
|
|||
.. rest_expand_all::
|
||||
|
||||
I am text, hear me roar!
|
||||
|
||||
==============
|
||||
List Servers
|
||||
==============
|
||||
|
||||
.. rest_method:: GET /servers
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- name: name
|
||||
- name2: name2
|
||||
- name3: name3
|
||||
|
||||
===========
|
||||
List Tags
|
||||
===========
|
||||
|
||||
.. rest_method:: GET /tags
|
||||
min_version: 2.17
|
||||
max_version: 2.19
|
|
@ -1,20 +0,0 @@
|
|||
name:
|
||||
in: body
|
||||
required: true
|
||||
type: string
|
||||
description: |
|
||||
The name of things
|
||||
name2:
|
||||
in: body
|
||||
required: true
|
||||
type: string
|
||||
description: |
|
||||
The name of things
|
||||
min_version: 2.11
|
||||
name3:
|
||||
in: body
|
||||
required: true
|
||||
type: string
|
||||
description: |
|
||||
The name of things
|
||||
max_version: 2.20
|
|
@ -1,35 +0,0 @@
|
|||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
# -- General configuration ----------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
|
||||
import openstackdocstheme
|
||||
|
||||
html_theme = 'openstackdocs'
|
||||
html_theme_path = [openstackdocstheme.get_html_theme_path()]
|
||||
html_theme_options = {
|
||||
"sidebar_mode": "toc",
|
||||
}
|
||||
|
||||
extensions = [
|
||||
'os_api_ref',
|
||||
]
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
|
@ -1 +0,0 @@
|
|||
# Empty parameter file
|
|
@ -1,55 +0,0 @@
|
|||
.. rest_expand_all::
|
||||
|
||||
I am text, hear me roar!
|
||||
|
||||
==============
|
||||
List Servers
|
||||
==============
|
||||
|
||||
.. rest_method:: GET /servers
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- name: name
|
||||
- name: lookup_key_name
|
||||
- name: name_1
|
||||
- invalid_name
|
||||
|
||||
|
||||
No Parameters Specified
|
||||
-----------------------
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
|
||||
|
||||
Empty File and Parameters Specified
|
||||
-----------------------------------
|
||||
|
||||
.. rest_parameters:: empty_parameters_file.yaml
|
||||
|
||||
- name: name
|
||||
|
||||
Nonexistent Parameter File
|
||||
--------------------------
|
||||
|
||||
.. rest_parameters:: no_parameters.yaml
|
||||
|
||||
|
||||
Check missing path parameters in stanza
|
||||
---------------------------------------
|
||||
|
||||
.. rest_method:: GET /server/{server_id}/{new_id}/{new_id2}
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- server_id: server_id
|
||||
|
||||
Check another missing path parameters in stanza
|
||||
-----------------------------------------------
|
||||
|
||||
.. rest_method:: GET /server/{b_id}/{c_id2}/{server_id}
|
||||
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- server_id: server_id
|
|
@ -1,25 +0,0 @@
|
|||
# valid path parameter
|
||||
server_id:
|
||||
description: |
|
||||
ID for server.
|
||||
in: path
|
||||
required: true
|
||||
type: string
|
||||
# These are out of order, this should be a warning.
|
||||
name2:
|
||||
in: body
|
||||
required: false
|
||||
type: string
|
||||
description: |
|
||||
foo
|
||||
name:
|
||||
in: body
|
||||
required: true
|
||||
type: string
|
||||
description: |
|
||||
The name of things
|
||||
name_1:
|
||||
description: |
|
||||
name_1 is missing type field.
|
||||
in: body
|
||||
required: true
|
|
@ -1,163 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
test_os_api_ref
|
||||
----------------------------------
|
||||
|
||||
Tests for `os_api_ref` module.
|
||||
"""
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
from sphinx_testing import with_app
|
||||
|
||||
from os_api_ref.tests import base
|
||||
|
||||
|
||||
class TestBasicExample(base.TestCase):
|
||||
"""Test basic rendering.
|
||||
|
||||
This can be used to test that basic rendering works for these
|
||||
examples, so if someone breaks something we know.
|
||||
"""
|
||||
|
||||
@with_app(buildername='html', srcdir=base.example_dir('basic'),
|
||||
copy_srcdir_to_tmpdir=True)
|
||||
def setUp(self, app, status, warning):
|
||||
super(TestBasicExample, self).setUp()
|
||||
self.app = app
|
||||
self.status = status
|
||||
self.warning = warning
|
||||
self.app.build()
|
||||
self.html = (app.outdir / 'index.html').read_text()
|
||||
self.soup = BeautifulSoup(self.html, 'html.parser')
|
||||
self.content = str(self.soup)
|
||||
|
||||
def test_expand_all(self):
|
||||
"""Do we get an expand all button like we expect."""
|
||||
content = str(self.soup.find(id='expand-all'))
|
||||
example_button = ('<button class="btn btn-info btn-sm btn-expand-all" '
|
||||
'data-toggle="collapse" id="expand-all">'
|
||||
'Show All</button>')
|
||||
self.assertEqual(
|
||||
example_button,
|
||||
content)
|
||||
|
||||
def test_rest_method(self):
|
||||
"""Do we get a REST method call block"""
|
||||
|
||||
# TODO(sdague): it probably would make sense to do this as a
|
||||
# whole template instead of parts.
|
||||
content = str(self.soup.find_all(class_='operation-grp'))
|
||||
self.assertIn(
|
||||
'<span class="glyphicon glyphicon-link"></span>',
|
||||
str(content))
|
||||
self.assertIn(
|
||||
'<span class="label label-GET">GET</span>',
|
||||
str(content))
|
||||
self.assertIn(
|
||||
'<div class="row col-md-12">/servers</div>',
|
||||
str(content))
|
||||
self.assertIn(
|
||||
('<button class="btn btn-info btn-sm btn-detail" '
|
||||
'data-target="#list-servers-detail" data-toggle="collapse" '
|
||||
'id="list-servers-detail-btn">detail</button>'),
|
||||
str(content))
|
||||
|
||||
def test_parameters(self):
|
||||
"""Do we get some parameters table"""
|
||||
|
||||
table = """<table border="1" class="docutils">
|
||||
<colgroup>
|
||||
<col width="20%"/>
|
||||
<col width="10%"/>
|
||||
<col width="10%"/>
|
||||
<col width="60%"/>
|
||||
</colgroup>
|
||||
<thead valign="bottom">
|
||||
<tr class="row-odd"><th class="head">Name</th>
|
||||
<th class="head">In</th>
|
||||
<th class="head">Type</th>
|
||||
<th class="head">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody valign="top">
|
||||
<tr class="row-even"><td>name</td>
|
||||
<td>body</td>
|
||||
<td>string</td>
|
||||
<td>The name of things</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>"""
|
||||
|
||||
self.assertIn(table, self.content)
|
||||
|
||||
def test_rest_response(self):
|
||||
|
||||
success_table = """table border="1" class="docutils">
|
||||
<colgroup>
|
||||
<col width="30%"/>
|
||||
<col width="70%"/>
|
||||
</colgroup>
|
||||
<thead valign="bottom">
|
||||
<tr class="row-odd"><th class="head">Code</th>
|
||||
<th class="head">Reason</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody valign="top">
|
||||
<tr class="row-even"><td><code>200 - OK</code></td>
|
||||
<td>Request was successful.</td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td><code>100 - Continue</code></td>
|
||||
<td>An unusual code for an API</td>
|
||||
</tr>
|
||||
<tr class="row-even"><td><code>201 - Created</code></td>
|
||||
<td>Resource was created and is ready to use.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
"""
|
||||
|
||||
error_table = """<table border="1" class="docutils">
|
||||
<colgroup>
|
||||
<col width="30%"/>
|
||||
<col width="70%"/>
|
||||
</colgroup>
|
||||
<thead valign="bottom">
|
||||
<tr class="row-odd"><th class="head">Code</th>
|
||||
<th class="head">Reason</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody valign="top">
|
||||
<tr class="row-even"><td><code>405 - Method Not Allowed</code></td>
|
||||
<td>Method is not valid for this endpoint.</td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td><code>403 - Forbidden</code></td>
|
||||
<td>Policy does not allow current user to do this operation.</td>
|
||||
</tr>
|
||||
<tr class="row-even"><td><code>401 - Unauthorized</code></td>
|
||||
<td>User must authenticate before making a request</td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td><code>400 - Bad Request</code></td>
|
||||
<td>Some content in the request was invalid</td>
|
||||
</tr>
|
||||
<tr class="row-even"><td><code>500 - Internal Server Error</code></td>
|
||||
<td>Something went wrong inside the service.</td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td><code>409 - Conflict</code></td>
|
||||
<td>There is already a zone with this name.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
"""
|
||||
self.assertIn(success_table, self.content)
|
||||
self.assertIn(error_table, self.content)
|
|
@ -1,106 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
test_os_api_ref
|
||||
----------------------------------
|
||||
|
||||
Tests for `os_api_ref` module.
|
||||
"""
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
from sphinx_testing import with_app
|
||||
|
||||
from os_api_ref.tests import base
|
||||
|
||||
|
||||
class TestMicroversions(base.TestCase):
|
||||
"""Test basic rendering.
|
||||
|
||||
This can be used to test that basic rendering works for these
|
||||
examples, so if someone breaks something we know.
|
||||
"""
|
||||
|
||||
@with_app(buildername='html', srcdir=base.example_dir('microversions'),
|
||||
copy_srcdir_to_tmpdir=True)
|
||||
def setUp(self, app, status, warning):
|
||||
super(TestMicroversions, self).setUp()
|
||||
self.app = app
|
||||
self.app.build()
|
||||
self.status = status.getvalue()
|
||||
self.warning = warning.getvalue()
|
||||
self.html = (app.outdir / 'index.html').read_text()
|
||||
self.soup = BeautifulSoup(self.html, 'html.parser')
|
||||
self.content = str(self.soup)
|
||||
|
||||
def test_rest_method(self):
|
||||
"""Test that min / max mv css class attributes are set"""
|
||||
content = self.soup.find_all(class_='rp_min_ver_2_17')
|
||||
self.assertIn(
|
||||
'<div class="operation-grp rp_min_ver_2_17 rp_max_ver_2_19 ">',
|
||||
str(content[0]))
|
||||
content = self.soup.find_all(class_='rp_max_ver_2_19')
|
||||
self.assertIn(
|
||||
'<div class="operation-grp rp_min_ver_2_17 rp_max_ver_2_19 ">',
|
||||
str(content[0]))
|
||||
|
||||
def test_parameters_table(self):
|
||||
"""Test that min / max mv css class attributes are set in params"""
|
||||
table = """<div class="api-detail collapse section" id="list-servers-detail">
|
||||
<table border="1" class="docutils">
|
||||
<colgroup>
|
||||
<col width="20%"/>
|
||||
<col width="10%"/>
|
||||
<col width="10%"/>
|
||||
<col width="60%"/>
|
||||
</colgroup>
|
||||
<thead valign="bottom">
|
||||
<tr class="row-odd"><th class="head">Name</th>
|
||||
<th class="head">In</th>
|
||||
<th class="head">Type</th>
|
||||
<th class="head">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody valign="top">
|
||||
<tr class="row-even"><td>name</td>
|
||||
<td>body</td>
|
||||
<td>string</td>
|
||||
<td>The name of things</td>
|
||||
</tr>
|
||||
<tr class="rp_min_ver_2_11 row-odd"><td>name2</td>
|
||||
<td>body</td>
|
||||
<td>string</td>
|
||||
<td><p class="first">The name of things</p>
|
||||
<p class="last"><strong>New in version 2.11</strong></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="rp_max_ver_2_20 row-even"><td>name3</td>
|
||||
<td>body</td>
|
||||
<td>string</td>
|
||||
<td><p class="first">The name of things</p>
|
||||
<p class="last"><strong>Deprecated in version 2.20</strong></p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
""" # noqa
|
||||
self.assertIn(table, self.content)
|
||||
|
||||
def test_mv_selector(self):
|
||||
|
||||
button_selectors = '<option selected="selected" value="">All</option><option value="2.1">2.1</option><option value="2.2">2.2</option><option value="2.3">2.3</option><option value="2.4">2.4</option><option value="2.5">2.5</option><option value="2.6">2.6</option><option value="2.7">2.7</option><option value="2.8">2.8</option><option value="2.9">2.9</option><option value="2.10">2.10</option><option value="2.11">2.11</option><option value="2.12">2.12</option><option value="2.13">2.13</option><option value="2.14">2.14</option><option value="2.15">2.15</option><option value="2.16">2.16</option><option value="2.17">2.17</option><option value="2.18">2.18</option><option value="2.19">2.19</option><option value="2.20">2.20</option><option value="2.21">2.21</option><option value="2.22">2.22</option><option value="2.23">2.23</option><option value="2.24">2.24</option><option value="2.25">2.25</option><option value="2.26">2.26</option><option value="2.27">2.27</option><option value="2.28">2.28</option><option value="2.29">2.29</option><option value="2.30">2.30</option>' # noqa
|
||||
self.assertIn(button_selectors, self.content)
|
||||
|
||||
def test_js_declares(self):
|
||||
self.assertIn("os_max_mv = 30;", self.content)
|
||||
self.assertIn("os_min_mv = 1;", self.content)
|
|
@ -1,26 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
test_os_api_ref
|
||||
----------------------------------
|
||||
|
||||
Tests for `os_api_ref` module.
|
||||
"""
|
||||
|
||||
from os_api_ref.tests import base
|
||||
|
||||
|
||||
class TestOs_api_ref(base.TestCase):
|
||||
|
||||
def test_something(self):
|
||||
pass
|
|
@ -1,101 +0,0 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
test_os_api_ref
|
||||
----------------------------------
|
||||
|
||||
Tests for `os_api_ref` module.
|
||||
"""
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
from sphinx_testing import with_app
|
||||
|
||||
from os_api_ref.tests import base
|
||||
|
||||
|
||||
class TestWarnings(base.TestCase):
|
||||
"""Test basic rendering.
|
||||
|
||||
This can be used to test that basic rendering works for these
|
||||
examples, so if someone breaks something we know.
|
||||
"""
|
||||
|
||||
@with_app(buildername='html', srcdir=base.example_dir('warnings'),
|
||||
copy_srcdir_to_tmpdir=True)
|
||||
def setUp(self, app, status, warning):
|
||||
super(TestWarnings, self).setUp()
|
||||
self.app = app
|
||||
self.app.build()
|
||||
self.status = status.getvalue()
|
||||
self.warning = warning.getvalue()
|
||||
self.html = (app.outdir / 'index.html').read_text()
|
||||
self.soup = BeautifulSoup(self.html, 'html.parser')
|
||||
self.content = str(self.soup)
|
||||
|
||||
def test_out_of_order(self):
|
||||
"""Do we get an out of order naming warning."""
|
||||
self.assertIn(
|
||||
("WARNING: Parameters out of order ``name2`` "
|
||||
"should be after ``name``"),
|
||||
self.warning)
|
||||
|
||||
def test_missing_lookup_name(self):
|
||||
"""Warning when missing a lookup key in parameter file."""
|
||||
self.assertIn(
|
||||
("WARNING: No field definition for ``lookup_key_name`` found in "),
|
||||
self.warning)
|
||||
|
||||
def test_missing_field(self):
|
||||
"""Warning when missing type field in parameter file."""
|
||||
self.assertIn(
|
||||
("WARNING: Failure on key: name, values: "
|
||||
+ "OrderedDict([('description',"
|
||||
+ " 'name_1 is missing type field.\\n'), ('in', 'body'),"
|
||||
+ " ('required', True)]). "
|
||||
+ "'NoneType' object has no attribute 'split'"),
|
||||
self.warning)
|
||||
|
||||
def test_invalid_parameter_definition(self):
|
||||
"""Warning when parameter definition is invalid."""
|
||||
self.assertIn(
|
||||
("WARNING: Invalid parameter definition ``invalid_name``. "
|
||||
+ "Expected format: ``name: reference``. "),
|
||||
self.warning)
|
||||
|
||||
def test_empty_parameter_file(self):
|
||||
"""Warning when parameter file exists but is empty."""
|
||||
self.assertIn(
|
||||
("WARNING: Parameters file is empty"),
|
||||
self.warning)
|
||||
|
||||
def test_no_parameters_set(self):
|
||||
"""Error when parameters are not set in rest_parameters stanza."""
|
||||
self.assertIn(
|
||||
("No parameters defined\n\n.."
|
||||
+ " rest_parameters:: parameters.yaml"),
|
||||
self.warning)
|
||||
|
||||
def test_parameter_file_not_exist(self):
|
||||
"""Error when parameter file does not exist"""
|
||||
self.assertIn(
|
||||
("No parameters defined\n\n.."
|
||||
+ " rest_parameters:: no_parameters.yaml"),
|
||||
self.warning)
|
||||
|
||||
def test_missing_path_parameter_in_stanza(self):
|
||||
"""Warning when path param not found in rest_parameter stanza."""
|
||||
|
||||
self.assertIn(
|
||||
("WARNING: No path parameter ``b_id`` found in"
|
||||
+ " rest_parameter stanza.\n"),
|
||||
self.warning)
|
|
@ -1,9 +0,0 @@
|
|||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
|
||||
pbr!=2.1.0,>=2.0.0 # Apache-2.0
|
||||
PyYAML>=3.10.0 # MIT
|
||||
six>=1.9.0 # MIT
|
||||
sphinx!=1.6.1,>=1.5.1 # BSD
|
||||
openstackdocstheme>=1.5.0 # Apache-2.0
|
46
setup.cfg
46
setup.cfg
|
@ -1,46 +0,0 @@
|
|||
[metadata]
|
||||
name = os-api-ref
|
||||
summary = Sphinx Extensions to support API reference sites in OpenStack
|
||||
description-file =
|
||||
README.rst
|
||||
author = OpenStack
|
||||
author-email = openstack-dev@lists.openstack.org
|
||||
home-page = http://docs.openstack.org/developer/os-api-ref
|
||||
classifier =
|
||||
Environment :: OpenStack
|
||||
Intended Audience :: Information Technology
|
||||
Intended Audience :: System Administrators
|
||||
License :: OSI Approved :: Apache Software License
|
||||
Operating System :: POSIX :: Linux
|
||||
Programming Language :: Python
|
||||
Programming Language :: Python :: 2
|
||||
Programming Language :: Python :: 2.7
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python :: 3.4
|
||||
|
||||
[files]
|
||||
packages =
|
||||
os_api_ref
|
||||
|
||||
[build_sphinx]
|
||||
source-dir = doc/source
|
||||
build-dir = doc/build
|
||||
all_files = 1
|
||||
warning-is-error = 1
|
||||
|
||||
[upload_sphinx]
|
||||
upload-dir = doc/build/html
|
||||
|
||||
[compile_catalog]
|
||||
directory = os_api_ref/locale
|
||||
domain = os_api_ref
|
||||
|
||||
[update_catalog]
|
||||
domain = os_api_ref
|
||||
output_dir = os_api_ref/locale
|
||||
input_file = os_api_ref/locale/os_api_ref.pot
|
||||
|
||||
[extract_messages]
|
||||
keywords = _ gettext ngettext l_ lazy_gettext
|
||||
mapping_file = babel.cfg
|
||||
output_file = os_api_ref/locale/os_api_ref.pot
|
29
setup.py
29
setup.py
|
@ -1,29 +0,0 @@
|
|||
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
|
||||
import setuptools
|
||||
|
||||
# In python < 2.7.4, a lazy loading of package `pbr` will break
|
||||
# setuptools if some other modules registered functions in `atexit`.
|
||||
# solution from: http://bugs.python.org/issue15881#msg170215
|
||||
try:
|
||||
import multiprocessing # noqa
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
setuptools.setup(
|
||||
setup_requires=['pbr>=2.0.0'],
|
||||
pbr=True)
|
|
@ -1,13 +0,0 @@
|
|||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
|
||||
hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
|
||||
|
||||
coverage!=4.4,>=4.0 # Apache-2.0
|
||||
python-subunit>=0.0.18 # Apache-2.0/BSD
|
||||
oslosphinx>=4.7.0 # Apache-2.0
|
||||
testrepository>=0.0.18 # Apache-2.0/BSD
|
||||
testtools>=1.4.0 # MIT
|
||||
sphinx-testing # BSD License
|
||||
beautifulsoup4 # MIT
|
|
@ -1,30 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Client constraint file contains this client version pin that is in conflict
|
||||
# with installing the client from source. We should remove the version pin in
|
||||
# the constraints file before applying it for from-source installation.
|
||||
|
||||
CONSTRAINTS_FILE="$1"
|
||||
shift 1
|
||||
|
||||
set -e
|
||||
|
||||
# NOTE(tonyb): Place this in the tox enviroment's log dir so it will get
|
||||
# published to logs.openstack.org for easy debugging.
|
||||
localfile="$VIRTUAL_ENV/log/upper-constraints.txt"
|
||||
|
||||
if [[ "$CONSTRAINTS_FILE" != http* ]]; then
|
||||
CONSTRAINTS_FILE="file://$CONSTRAINTS_FILE"
|
||||
fi
|
||||
# NOTE(tonyb): need to add curl to bindep.txt if the project supports bindep
|
||||
curl "$CONSTRAINTS_FILE" --insecure --progress-bar --output "$localfile"
|
||||
|
||||
pip install -c"$localfile" openstack-requirements
|
||||
|
||||
# This is the main purpose of the script: Allow local installation of
|
||||
# the current repo. It is listed in constraints file and thus any
|
||||
# install will be constrained and we need to unconstrain it.
|
||||
edit-constraints "$localfile" -- "$CLIENT_NAME"
|
||||
|
||||
pip install -c"$localfile" -U "$@"
|
||||
exit $?
|
38
tox.ini
38
tox.ini
|
@ -1,38 +0,0 @@
|
|||
[tox]
|
||||
minversion = 2.0
|
||||
envlist = py27,pep8,docs
|
||||
skipsdist = True
|
||||
|
||||
[testenv]
|
||||
setenv =
|
||||
VIRTUAL_ENV={envdir}
|
||||
BRANCH_NAME=master
|
||||
CLIENT_NAME=os-api-ref
|
||||
install_command = {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages}
|
||||
pip install -U {opts} {packages}
|
||||
deps = -r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
commands = python setup.py test --slowest --no-parallel --testr-args='{posargs}'
|
||||
|
||||
[testenv:pep8]
|
||||
commands = flake8 {posargs}
|
||||
|
||||
[testenv:venv]
|
||||
commands = {posargs}
|
||||
|
||||
[testenv:cover]
|
||||
commands = python setup.py test --coverage --testr-args='{posargs}'
|
||||
|
||||
[testenv:docs]
|
||||
commands = python setup.py build_sphinx
|
||||
|
||||
[testenv:debug]
|
||||
commands = oslo_debug_helper {posargs}
|
||||
|
||||
[flake8]
|
||||
# E123, E125 skipped as they are invalid PEP-8.
|
||||
|
||||
show-source = True
|
||||
ignore = E123,E125,E129
|
||||
builtins = _
|
||||
exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build
|
Loading…
Reference in New Issue