Skip to content

chore: add scripts to update discovery artifacts #1286

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
0cd2754
chore: add scripts to update discovery artifacts
yoshi-automation Apr 7, 2021
9fb58c5
Update comment to reflect code
yoshi-automation Apr 8, 2021
555d2b3
Use keyword arguments for pd.Dataframe().rename_axis()
yoshi-automation Apr 8, 2021
5a6cf31
Remove call to `pd.Dataframe().columns.droplevel(1)`
yoshi-automation Apr 11, 2021
dae470d
Allow the directory used for storing intermediate files to be changed…
yoshi-automation Apr 11, 2021
67b01e2
Rename column 'Percent' to 'Proportion' to reflect usage.
yoshi-automation Apr 11, 2021
170d9db
Only configure git when running the script via Github Action
parthea Apr 11, 2021
39a4efb
Fix git configuration when using github action
parthea Apr 11, 2021
0207e19
Write discovery artifact if no revision is found
parthea Apr 13, 2021
901620a
Update comment to reflect logic
parthea Apr 14, 2021
632a683
Update code comment to be more descriptive.
parthea Apr 14, 2021
d1c2e51
Fix formatting. Update docstring to be more descriptive.
parthea Apr 14, 2021
9917d98
Fix formatting in docstring.
parthea Apr 14, 2021
eceaf51
Improve performance by reducing file operations.
parthea Apr 14, 2021
c93d3f3
Fix formatting.
parthea Apr 14, 2021
9054581
Add check to ensure temp directory exists
parthea Apr 14, 2021
9aa6ad1
Remove check in ChangeSummary for `file_list` arg empty
parthea Apr 14, 2021
718e02f
Update code to reflect comment
parthea Apr 14, 2021
075626c
Add tests for changesummary.py . Only 61% coverage so far.
parthea Apr 14, 2021
1db7e6a
Fix initialization of tests
parthea Apr 14, 2021
e7e9e88
Add tests
parthea Apr 15, 2021
5414ee2
Extract regex that determines whether version is stable into separate…
parthea Apr 15, 2021
10de0c4
Move describe.py back to original location
parthea Apr 15, 2021
dad3aa3
Set scripts coverage to 80%
parthea Apr 15, 2021
9202d32
Update relative paths in describe.py
parthea Apr 15, 2021
4ed97ca
Update README in the scripts dir to mention adding a summary to the P…
parthea Apr 15, 2021
fc52778
Remove X-USER-IP header
parthea Apr 21, 2021
8c25e1f
Use `pathlib` instead of `os`
parthea Apr 21, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 105 additions & 72 deletions describe.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import argparse
import collections
import json
import os
import pathlib
import re
import string
import sys
Expand All @@ -37,12 +37,15 @@
from googleapiclient.discovery import build
from googleapiclient.discovery import build_from_document
from googleapiclient.discovery import UnknownApiNameOrVersion
from googleapiclient.discovery_cache import get_static_doc
from googleapiclient.http import build_http
from googleapiclient.errors import HttpError

import uritemplate

DISCOVERY_DOC_DIR = (
pathlib.Path(__file__).parent.resolve() / "googleapiclient" / "discovery_cache" / "documents"
)

CSS = """<style>

body, h1, h2, h3, div, span, p, pre, a {
Expand Down Expand Up @@ -133,7 +136,7 @@
<code><a href="#$name">$name($params)</a></code></p>
<p class="firstline">$firstline</p>"""

BASE = "docs/dyn"
BASE = pathlib.Path(__file__).parent.resolve() / "docs" / "dyn"

DIRECTORY_URI = "https://www.googleapis.com/discovery/v1/apis"

Expand Down Expand Up @@ -254,14 +257,10 @@ def method(name, doc):
name: string, Name of the method.
doc: string, The methods docstring.
"""
import html

params = method_params(doc)
if sys.version_info.major >= 3:
import html
doc = html.escape(doc)
else:
import cgi
doc = cgi.escape(doc)
doc = html.escape(doc)
return string.Template(METHOD_TEMPLATE).substitute(
name=name, params=params, doc=doc
)
Expand Down Expand Up @@ -358,13 +357,10 @@ def document_collection(resource, path, root_discovery, discovery, css=CSS):
return "\n".join(html)


def document_collection_recursive(resource, path, root_discovery, discovery):

def document_collection_recursive(resource, path, root_discovery, discovery, doc_destination_dir):
html = document_collection(resource, path, root_discovery, discovery)

f = open(os.path.join(FLAGS.dest, path + "html"), "w")
if sys.version_info.major < 3:
html = html.encode("utf-8")
f = open(pathlib.Path(doc_destination_dir).joinpath(path + "html"), "w")

f.write(html)
f.close()
Expand All @@ -383,44 +379,76 @@ def document_collection_recursive(resource, path, root_discovery, discovery):
path + name + ".",
root_discovery,
discovery["resources"].get(dname, {}),
doc_destination_dir
)


def document_api(name, version, uri):
def document_api(name, version, uri, doc_destination_dir):
"""Document the given API.

Args:
name: string, Name of the API.
version: string, Version of the API.
uri: string, URI of the API's discovery document
Args:
name (str): Name of the API.
version (str): Version of the API.
uri (str): URI of the API's discovery document
doc_destination_dir (str): relative path where the reference
documentation should be saved.
"""
try:
service = build(name, version)
content = get_static_doc(name, version)
except UnknownApiNameOrVersion as e:
print("Warning: {} {} found but could not be built.".format(name, version))
http = build_http()
resp, content = http.request(
uri or uritemplate.expand(
FLAGS.discovery_uri_template, {"api": name, "apiVersion": version}
)
)

if resp.status == 200:
discovery = json.loads(content)
service = build_from_document(discovery)
version = safe_version(version)
doc_name = "{}.{}.json".format(name, version.replace("_", ""))

discovery_file_path = DISCOVERY_DOC_DIR / doc_name
revision = None

pathlib.Path(discovery_file_path).touch(exist_ok=True)

# Write discovery artifact to disk if revision equal or newer
with open(discovery_file_path, "r+") as f:
try:
json_data = json.load(f)
revision = json_data['revision']
except json.JSONDecodeError:
revision = None

if revision is None or discovery['revision'] >= revision:
# Reset position to the beginning
f.seek(0)
# Write the changes to disk
json.dump(discovery, f, indent=2, sort_keys=True)
# Truncate anything left as it's not needed
f.truncate()

elif resp.status == 404:
print("Warning: {} {} not found. HTTP Code: {}".format(name, version, resp.status))
return
except HttpError as e:
print("Warning: {} {} returned {}.".format(name, version, e))
else:
print("Warning: {} {} could not be built. HTTP Code: {}".format(name, version, resp.status))
return

discovery = json.loads(content)

version = safe_version(version)

document_collection_recursive(
service, "{}_{}.".format(name, version), discovery, discovery
service, "{}_{}.".format(name, version), discovery, discovery, doc_destination_dir
)


def document_api_from_discovery_document(uri):
def document_api_from_discovery_document(discovery_url, doc_destination_dir):
"""Document the given API.

Args:
uri: string, URI of discovery document.
discovery_url (str): URI of discovery document.
doc_destination_dir (str): relative path where the reference
documentation should be saved.
"""
http = build_http()
response, content = http.request(FLAGS.discovery_uri)
response, content = http.request(discovery_url)
discovery = json.loads(content)

service = build_from_document(discovery)
Expand All @@ -429,48 +457,53 @@ def document_api_from_discovery_document(uri):
version = safe_version(discovery["version"])

document_collection_recursive(
service, "{}_{}.".format(name, version), discovery, discovery
service, "{}_{}.".format(name, version), discovery, discovery, doc_destination_dir
)

def generate_all_api_documents(directory_uri=DIRECTORY_URI, doc_destination_dir=BASE):
""" Retrieve discovery artifacts and fetch reference documentations
for all apis listed in the public discovery directory.
args:
directory_uri (str): uri of the public discovery directory.
doc_destination_dir (str): relative path where the reference
documentation should be saved.
"""
api_directory = collections.defaultdict(list)
http = build_http()
resp, content = http.request(directory_uri)
if resp.status == 200:
directory = json.loads(content)["items"]
for api in directory:
document_api(api["name"], api["version"], api["discoveryRestUrl"], doc_destination_dir)
api_directory[api["name"]].append(api["version"])

# sort by api name and version number
for api in api_directory:
api_directory[api] = sorted(api_directory[api])
api_directory = OrderedDict(
sorted(api_directory.items(), key=lambda x: x[0])
)

markdown = []
for api, versions in api_directory.items():
markdown.append("## %s" % api)
for version in versions:
markdown.append(
"* [%s](http://googleapis.github.io/google-api-python-client/docs/dyn/%s_%s.html)"
% (version, api, safe_version(version))
)
markdown.append("\n")

with open(BASE / "index.md", "w") as f:
markdown = "\n".join(markdown)
f.write(markdown)

else:
sys.exit("Failed to load the discovery document.")

if __name__ == "__main__":
FLAGS = parser.parse_args(sys.argv[1:])
if FLAGS.discovery_uri:
document_api_from_discovery_document(FLAGS.discovery_uri)
document_api_from_discovery_document(discovery_url=FLAGS.discovery_uri, doc_destination_dir=FLAGS.dest)
else:
api_directory = collections.defaultdict(list)
http = build_http()
resp, content = http.request(
FLAGS.directory_uri, headers={"X-User-IP": "0.0.0.0"}
)
if resp.status == 200:
directory = json.loads(content)["items"]
for api in directory:
document_api(api["name"], api["version"], api["discoveryRestUrl"])
api_directory[api["name"]].append(api["version"])

# sort by api name and version number
for api in api_directory:
api_directory[api] = sorted(api_directory[api])
api_directory = OrderedDict(
sorted(api_directory.items(), key=lambda x: x[0])
)

markdown = []
for api, versions in api_directory.items():
markdown.append("## %s" % api)
for version in versions:
markdown.append(
"* [%s](http://googleapis.github.io/google-api-python-client/docs/dyn/%s_%s.html)"
% (version, api, safe_version(version))
)
markdown.append("\n")

with open("docs/dyn/index.md", "w") as f:
markdown = "\n".join(markdown)
if sys.version_info.major < 3:
markdown = markdown.encode("utf-8")
f.write(markdown)

else:
sys.exit("Failed to load the discovery document.")
generate_all_api_documents(directory_uri=FLAGS.directory_uri, doc_destination_dir=FLAGS.dest)
19 changes: 19 additions & 0 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import os
import sys

import nox
Expand Down Expand Up @@ -90,3 +91,21 @@ def unit(session, oauth2client):
"tests",
*session.posargs,
)

@nox.session(python=["3.9"])
def scripts(session):
session.install(*test_dependencies)
session.install("-e", ".")
session.install("-r", "scripts/requirements.txt")

# Run py.test against the unit tests.
session.run(
"py.test",
"--quiet",
"--cov=scripts",
"--cov-config=.coveragerc",
"--cov-report=",
"--cov-fail-under=80",
"scripts",
*session.posargs,
)
21 changes: 21 additions & 0 deletions scripts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Discovery Artifact Automation
Discovery Artifacts are automatically updated using a Github Action script. This
documentation is intended for users that need to maintain the repository.

## Updating discovery artifacts locally

To update discovery artifacts locally:
1. Create a virtual environment using `pyenv virtualenv updateartifacts`
2. Activate the virtual environment using `pyenv activate updateartifacts`
3. Clone the repository, and `cd` into the `scripts` directory
4. Run `pip install -r requirements.txt`
5. Run `pip install -e ../`
6. Run `git checkout -b update-discovery-artifacts-manual`
7. Run `python3 updatediscoveryartifacts.py`
8. Run `./createcommits.sh`
9. Run `python3 buildprbody.py`
10. Create a pull request with the changes.
11. Copy the contents of `temp/allapis.summary` into the PR Body.

## Questions
Feel free to submit an issue!
Loading