Initial commit (Clean history)

This commit is contained in:
anhduy-tech
2025-12-30 11:27:14 +07:00
commit ef48c93de0
19255 changed files with 3248867 additions and 0 deletions

View File

@@ -0,0 +1,61 @@
# Copyright 2017 Google Inc.
#
# 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.
"""Utilities for Google Media Downloads and Resumable Uploads.
This package has some general purposes modules, e.g.
:mod:`~google.resumable_media.common`, but the majority of the
public interface will be contained in subpackages.
===========
Subpackages
===========
Each subpackage is tailored to a specific transport library:
* the :mod:`~google.resumable_media.requests` subpackage uses the ``requests``
transport library.
.. _requests: http://docs.python-requests.org/
==========
Installing
==========
To install with `pip`_:
.. code-block:: console
$ pip install --upgrade google-resumable-media
.. _pip: https://pip.pypa.io/
"""
from google.resumable_media.common import DataCorruption
from google.resumable_media.common import InvalidResponse
from google.resumable_media.common import PERMANENT_REDIRECT
from google.resumable_media.common import RetryStrategy
from google.resumable_media.common import TOO_MANY_REQUESTS
from google.resumable_media.common import UPLOAD_CHUNK_SIZE
__all__ = [
"DataCorruption",
"InvalidResponse",
"PERMANENT_REDIRECT",
"RetryStrategy",
"TOO_MANY_REQUESTS",
"UPLOAD_CHUNK_SIZE",
]

View File

@@ -0,0 +1,550 @@
# Copyright 2017 Google Inc.
#
# 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.
"""Virtual bases classes for downloading media from Google APIs."""
import http.client
import re
from google._async_resumable_media import _helpers
from google.resumable_media import common
_CONTENT_RANGE_RE = re.compile(
r"bytes (?P<start_byte>\d+)-(?P<end_byte>\d+)/(?P<total_bytes>\d+)",
flags=re.IGNORECASE,
)
_ACCEPTABLE_STATUS_CODES = (http.client.OK, http.client.PARTIAL_CONTENT)
_GET = "GET"
_ZERO_CONTENT_RANGE_HEADER = "bytes */0"
class DownloadBase(object):
"""Base class for download helpers.
Defines core shared behavior across different download types.
Args:
media_url (str): The URL containing the media to be downloaded.
stream (IO[bytes]): A write-able stream (i.e. file-like object) that
the downloaded resource can be written to.
start (int): The first byte in a range to be downloaded.
end (int): The last byte in a range to be downloaded.
headers (Optional[Mapping[str, str]]): Extra headers that should
be sent with the request, e.g. headers for encrypted data.
Attributes:
media_url (str): The URL containing the media to be downloaded.
start (Optional[int]): The first byte in a range to be downloaded.
end (Optional[int]): The last byte in a range to be downloaded.
"""
def __init__(self, media_url, stream=None, start=None, end=None, headers=None):
self.media_url = media_url
self._stream = stream
self.start = start
self.end = end
if headers is None:
headers = {}
self._headers = headers
self._finished = False
self._retry_strategy = common.RetryStrategy()
@property
def finished(self):
"""bool: Flag indicating if the download has completed."""
return self._finished
@staticmethod
def _get_status_code(response):
"""Access the status code from an HTTP response.
Args:
response (object): The HTTP response object.
Raises:
NotImplementedError: Always, since virtual.
"""
raise NotImplementedError("This implementation is virtual.")
@staticmethod
def _get_headers(response):
"""Access the headers from an HTTP response.
Args:
response (object): The HTTP response object.
Raises:
NotImplementedError: Always, since virtual.
"""
raise NotImplementedError("This implementation is virtual.")
@staticmethod
def _get_body(response):
"""Access the response body from an HTTP response.
Args:
response (object): The HTTP response object.
Raises:
NotImplementedError: Always, since virtual.
"""
raise NotImplementedError("This implementation is virtual.")
class Download(DownloadBase):
"""Helper to manage downloading a resource from a Google API.
"Slices" of the resource can be retrieved by specifying a range
with ``start`` and / or ``end``. However, in typical usage, neither
``start`` nor ``end`` is expected to be provided.
Args:
media_url (str): The URL containing the media to be downloaded.
stream (IO[bytes]): A write-able stream (i.e. file-like object) that
the downloaded resource can be written to.
start (int): The first byte in a range to be downloaded. If not
provided, but ``end`` is provided, will download from the
beginning to ``end`` of the media.
end (int): The last byte in a range to be downloaded. If not
provided, but ``start`` is provided, will download from the
``start`` to the end of the media.
headers (Optional[Mapping[str, str]]): Extra headers that should
be sent with the request, e.g. headers for encrypted data.
checksum Optional([str]): The type of checksum to compute to verify
the integrity of the object. The response headers must contain
a checksum of the requested type. If the headers lack an
appropriate checksum (for instance in the case of transcoded or
ranged downloads where the remote service does not know the
correct checksum) an INFO-level log will be emitted. Supported
values are "md5", "crc32c" and None.
"""
def __init__(
self, media_url, stream=None, start=None, end=None, headers=None, checksum="md5"
):
super(Download, self).__init__(
media_url, stream=stream, start=start, end=end, headers=headers
)
self.checksum = checksum
def _prepare_request(self):
"""Prepare the contents of an HTTP request.
This is everything that must be done before a request that doesn't
require network I/O (or other I/O). This is based on the `sans-I/O`_
philosophy.
Returns:
Tuple[str, str, NoneType, Mapping[str, str]]: The quadruple
* HTTP verb for the request (always GET)
* the URL for the request
* the body of the request (always :data:`None`)
* headers for the request
Raises:
ValueError: If the current :class:`Download` has already
finished.
.. _sans-I/O: https://sans-io.readthedocs.io/
"""
if self.finished:
raise ValueError("A download can only be used once.")
add_bytes_range(self.start, self.end, self._headers)
return _GET, self.media_url, None, self._headers
def _process_response(self, response):
"""Process the response from an HTTP request.
This is everything that must be done after a request that doesn't
require network I/O (or other I/O). This is based on the `sans-I/O`_
philosophy.
Args:
response (object): The HTTP response object.
.. _sans-I/O: https://sans-io.readthedocs.io/
"""
# Tombstone the current Download so it cannot be used again.
self._finished = True
_helpers.require_status_code(
response, _ACCEPTABLE_STATUS_CODES, self._get_status_code
)
def consume(self, transport, timeout=None):
"""Consume the resource to be downloaded.
If a ``stream`` is attached to this download, then the downloaded
resource will be written to the stream.
Args:
transport (object): An object which can make authenticated
requests.
timeout (Optional[Union[float, aiohttp.ClientTimeout]]):
The number of seconds to wait for the server response.
Depending on the retry strategy, a request may be repeated
several times using the same timeout each time.
Can also be passed as an `aiohttp.ClientTimeout` object.
Raises:
NotImplementedError: Always, since virtual.
"""
raise NotImplementedError("This implementation is virtual.")
class ChunkedDownload(DownloadBase):
"""Download a resource in chunks from a Google API.
Args:
media_url (str): The URL containing the media to be downloaded.
chunk_size (int): The number of bytes to be retrieved in each
request.
stream (IO[bytes]): A write-able stream (i.e. file-like object) that
will be used to concatenate chunks of the resource as they are
downloaded.
start (int): The first byte in a range to be downloaded. If not
provided, defaults to ``0``.
end (int): The last byte in a range to be downloaded. If not
provided, will download to the end of the media.
headers (Optional[Mapping[str, str]]): Extra headers that should
be sent with each request, e.g. headers for data encryption
key headers.
Attributes:
media_url (str): The URL containing the media to be downloaded.
start (Optional[int]): The first byte in a range to be downloaded.
end (Optional[int]): The last byte in a range to be downloaded.
chunk_size (int): The number of bytes to be retrieved in each request.
Raises:
ValueError: If ``start`` is negative.
"""
def __init__(self, media_url, chunk_size, stream, start=0, end=None, headers=None):
if start < 0:
raise ValueError(
"On a chunked download the starting " "value cannot be negative."
)
super(ChunkedDownload, self).__init__(
media_url, stream=stream, start=start, end=end, headers=headers
)
self.chunk_size = chunk_size
self._bytes_downloaded = 0
self._total_bytes = None
self._invalid = False
@property
def bytes_downloaded(self):
"""int: Number of bytes that have been downloaded."""
return self._bytes_downloaded
@property
def total_bytes(self):
"""Optional[int]: The total number of bytes to be downloaded."""
return self._total_bytes
@property
def invalid(self):
"""bool: Indicates if the download is in an invalid state.
This will occur if a call to :meth:`consume_next_chunk` fails.
"""
return self._invalid
def _get_byte_range(self):
"""Determines the byte range for the next request.
Returns:
Tuple[int, int]: The pair of begin and end byte for the next
chunked request.
"""
curr_start = self.start + self.bytes_downloaded
curr_end = curr_start + self.chunk_size - 1
# Make sure ``curr_end`` does not exceed ``end``.
if self.end is not None:
curr_end = min(curr_end, self.end)
# Make sure ``curr_end`` does not exceed ``total_bytes - 1``.
if self.total_bytes is not None:
curr_end = min(curr_end, self.total_bytes - 1)
return curr_start, curr_end
def _prepare_request(self):
"""Prepare the contents of an HTTP request.
This is everything that must be done before a request that doesn't
require network I/O (or other I/O). This is based on the `sans-I/O`_
philosophy.
.. note:
This method will be used multiple times, so ``headers`` will
be mutated in between requests. However, we don't make a copy
since the same keys are being updated.
Returns:
Tuple[str, str, NoneType, Mapping[str, str]]: The quadruple
* HTTP verb for the request (always GET)
* the URL for the request
* the body of the request (always :data:`None`)
* headers for the request
Raises:
ValueError: If the current download has finished.
ValueError: If the current download is invalid.
.. _sans-I/O: https://sans-io.readthedocs.io/
"""
if self.finished:
raise ValueError("Download has finished.")
if self.invalid:
raise ValueError("Download is invalid and cannot be re-used.")
curr_start, curr_end = self._get_byte_range()
add_bytes_range(curr_start, curr_end, self._headers)
return _GET, self.media_url, None, self._headers
def _make_invalid(self):
"""Simple setter for ``invalid``.
This is intended to be passed along as a callback to helpers that
raise an exception so they can mark this instance as invalid before
raising.
"""
self._invalid = True
async def _process_response(self, response):
"""Process the response from an HTTP request.
This is everything that must be done after a request that doesn't
require network I/O. This is based on the `sans-I/O`_ philosophy.
For the time being, this **does require** some form of I/O to write
a chunk to ``stream``. However, this will (almost) certainly not be
network I/O.
Updates the current state after consuming a chunk. First,
increments ``bytes_downloaded`` by the number of bytes in the
``content-length`` header.
If ``total_bytes`` is already set, this assumes (but does not check)
that we already have the correct value and doesn't bother to check
that it agrees with the headers.
We expect the **total** length to be in the ``content-range`` header,
but this header is only present on requests which sent the ``range``
header. This response header should be of the form
``bytes {start}-{end}/{total}`` and ``{end} - {start} + 1``
should be the same as the ``Content-Length``.
Args:
response (object): The HTTP response object (need headers).
Raises:
~google.resumable_media.common.InvalidResponse: If the number
of bytes in the body doesn't match the content length header.
.. _sans-I/O: https://sans-io.readthedocs.io/
"""
# Verify the response before updating the current instance.
if _check_for_zero_content_range(
response, self._get_status_code, self._get_headers
):
self._finished = True
return
_helpers.require_status_code(
response,
_ACCEPTABLE_STATUS_CODES,
self._get_status_code,
callback=self._make_invalid,
)
headers = self._get_headers(response)
response_body = await self._get_body(response)
start_byte, end_byte, total_bytes = get_range_info(
response, self._get_headers, callback=self._make_invalid
)
transfer_encoding = headers.get("transfer-encoding")
if transfer_encoding is None:
content_length = _helpers.header_required(
response,
"content-length",
self._get_headers,
callback=self._make_invalid,
)
num_bytes = int(content_length)
if len(response_body) != num_bytes:
self._make_invalid()
raise common.InvalidResponse(
response,
"Response is different size than content-length",
"Expected",
num_bytes,
"Received",
len(response_body),
)
else:
# 'content-length' header not allowed with chunked encoding.
num_bytes = end_byte - start_byte + 1
# First update ``bytes_downloaded``.
self._bytes_downloaded += num_bytes
# If the end byte is past ``end`` or ``total_bytes - 1`` we are done.
if self.end is not None and end_byte >= self.end:
self._finished = True
elif end_byte >= total_bytes - 1:
self._finished = True
# NOTE: We only use ``total_bytes`` if not already known.
if self.total_bytes is None:
self._total_bytes = total_bytes
# Write the response body to the stream.
self._stream.write(response_body)
def consume_next_chunk(self, transport, timeout=None):
"""Consume the next chunk of the resource to be downloaded.
Args:
transport (object): An object which can make authenticated
requests.
timeout (Optional[Union[float, aiohttp.ClientTimeout]]):
The number of seconds to wait for the server response.
Depending on the retry strategy, a request may be repeated
several times using the same timeout each time.
Can also be passed as an `aiohttp.ClientTimeout` object.
Raises:
NotImplementedError: Always, since virtual.
"""
raise NotImplementedError("This implementation is virtual.")
def add_bytes_range(start, end, headers):
"""Add a bytes range to a header dictionary.
Some possible inputs and the corresponding bytes ranges::
>>> headers = {}
>>> add_bytes_range(None, None, headers)
>>> headers
{}
>>> add_bytes_range(500, 999, headers)
>>> headers['range']
'bytes=500-999'
>>> add_bytes_range(None, 499, headers)
>>> headers['range']
'bytes=0-499'
>>> add_bytes_range(-500, None, headers)
>>> headers['range']
'bytes=-500'
>>> add_bytes_range(9500, None, headers)
>>> headers['range']
'bytes=9500-'
Args:
start (Optional[int]): The first byte in a range. Can be zero,
positive, negative or :data:`None`.
end (Optional[int]): The last byte in a range. Assumed to be
positive.
headers (Mapping[str, str]): A headers mapping which can have the
bytes range added if at least one of ``start`` or ``end``
is not :data:`None`.
"""
if start is None:
if end is None:
# No range to add.
return
else:
# NOTE: This assumes ``end`` is non-negative.
bytes_range = "0-{:d}".format(end)
else:
if end is None:
if start < 0:
bytes_range = "{:d}".format(start)
else:
bytes_range = "{:d}-".format(start)
else:
# NOTE: This is invalid if ``start < 0``.
bytes_range = "{:d}-{:d}".format(start, end)
headers[_helpers.RANGE_HEADER] = "bytes=" + bytes_range
def get_range_info(response, get_headers, callback=_helpers.do_nothing):
"""Get the start, end and total bytes from a content range header.
Args:
response (object): An HTTP response object.
get_headers (Callable[Any, Mapping[str, str]]): Helper to get headers
from an HTTP response.
callback (Optional[Callable]): A callback that takes no arguments,
to be executed when an exception is being raised.
Returns:
Tuple[int, int, int]: The start byte, end byte and total bytes.
Raises:
~google.resumable_media.common.InvalidResponse: If the
``Content-Range`` header is not of the form
``bytes {start}-{end}/{total}``.
"""
content_range = _helpers.header_required(
response, _helpers.CONTENT_RANGE_HEADER, get_headers, callback=callback
)
match = _CONTENT_RANGE_RE.match(content_range)
if match is None:
callback()
raise common.InvalidResponse(
response,
"Unexpected content-range header",
content_range,
'Expected to be of the form "bytes {start}-{end}/{total}"',
)
return (
int(match.group("start_byte")),
int(match.group("end_byte")),
int(match.group("total_bytes")),
)
def _check_for_zero_content_range(response, get_status_code, get_headers):
"""Validate if response status code is 416 and content range is zero.
This is the special case for handling zero bytes files.
Args:
response (object): An HTTP response object.
get_status_code (Callable[Any, int]): Helper to get a status code
from a response.
get_headers (Callable[Any, Mapping[str, str]]): Helper to get headers
from an HTTP response.
Returns:
bool: True if content range total bytes is zero, false otherwise.
"""
if get_status_code(response) == http.client.REQUESTED_RANGE_NOT_SATISFIABLE:
content_range = _helpers.header_required(
response,
_helpers.CONTENT_RANGE_HEADER,
get_headers,
callback=_helpers.do_nothing,
)
if content_range == _ZERO_CONTENT_RANGE_HEADER:
return True
return False

View File

@@ -0,0 +1,197 @@
# Copyright 2020 Google Inc.
#
# 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.
"""Shared utilities used by both downloads and uploads."""
import logging
import random
import time
from google.resumable_media import common
RANGE_HEADER = "range"
CONTENT_RANGE_HEADER = "content-range"
_SLOW_CRC32C_WARNING = (
"Currently using crcmod in pure python form. This is a slow "
"implementation. Python 3 has a faster implementation, `google-crc32c`, "
"which will be used if it is installed."
)
_HASH_HEADER = "x-goog-hash"
_MISSING_CHECKSUM = """\
No {checksum_type} checksum was returned from the service while downloading {}
(which happens for composite objects), so client-side content integrity
checking is not being performed."""
_LOGGER = logging.getLogger(__name__)
def do_nothing():
"""Simple default callback."""
def header_required(response, name, get_headers, callback=do_nothing):
"""Checks that a specific header is in a headers dictionary.
Args:
response (object): An HTTP response object, expected to have a
``headers`` attribute that is a ``Mapping[str, str]``.
name (str): The name of a required header.
get_headers (Callable[Any, Mapping[str, str]]): Helper to get headers
from an HTTP response.
callback (Optional[Callable]): A callback that takes no arguments,
to be executed when an exception is being raised.
Returns:
str: The desired header.
Raises:
~google.resumable_media.common.InvalidResponse: If the header
is missing.
"""
headers = get_headers(response)
if name not in headers:
callback()
raise common.InvalidResponse(
response, "Response headers must contain header", name
)
return headers[name]
def require_status_code(response, status_codes, get_status_code, callback=do_nothing):
"""Require a response has a status code among a list.
Args:
response (object): The HTTP response object.
status_codes (tuple): The acceptable status codes.
get_status_code (Callable[Any, int]): Helper to get a status code
from a response.
callback (Optional[Callable]): A callback that takes no arguments,
to be executed when an exception is being raised.
Returns:
int: The status code.
Raises:
~google.resumable_media.common.InvalidResponse: If the status code
is not one of the values in ``status_codes``.
"""
status_code = get_status_code(response)
if status_code not in status_codes:
callback()
raise common.InvalidResponse(
response,
"Request failed with status code",
status_code,
"Expected one of",
*status_codes
)
return status_code
def calculate_retry_wait(base_wait, max_sleep):
"""Calculate the amount of time to wait before a retry attempt.
Wait time grows exponentially with the number of attempts, until
``max_sleep``.
A random amount of jitter (between 0 and 1 seconds) is added to spread out
retry attempts from different clients.
Args:
base_wait (float): The "base" wait time (i.e. without any jitter)
that will be doubled until it reaches the maximum sleep.
max_sleep (float): Maximum value that a sleep time is allowed to be.
Returns:
Tuple[float, float]: The new base wait time as well as the wait time
to be applied (with a random amount of jitter between 0 and 1 seconds
added).
"""
new_base_wait = 2.0 * base_wait
if new_base_wait > max_sleep:
new_base_wait = max_sleep
jitter_ms = random.randint(0, 1000)
return new_base_wait, new_base_wait + 0.001 * jitter_ms
async def wait_and_retry(func, get_status_code, retry_strategy):
"""Attempts to retry a call to ``func`` until success.
Expects ``func`` to return an HTTP response and uses ``get_status_code``
to check if the response is retry-able.
Will retry until :meth:`~.RetryStrategy.retry_allowed` (on the current
``retry_strategy``) returns :data:`False`. Uses
:func:`calculate_retry_wait` to double the wait time (with jitter) after
each attempt.
Args:
func (Callable): A callable that takes no arguments and produces
an HTTP response which will be checked as retry-able.
get_status_code (Callable[Any, int]): Helper to get a status code
from a response.
retry_strategy (~google.resumable_media.common.RetryStrategy): The
strategy to use if the request fails and must be retried.
Returns:
object: The return value of ``func``.
"""
total_sleep = 0.0
num_retries = 0
base_wait = 0.5 # When doubled will give 1.0
while True: # return on success or when retries exhausted.
error = None
try:
response = await func()
except ConnectionError as e:
error = e
else:
if get_status_code(response) not in common.RETRYABLE:
return response
if not retry_strategy.retry_allowed(total_sleep, num_retries):
# Retries are exhausted and no acceptable response was received. Raise the
# retriable_error or return the unacceptable response.
if error:
raise error
return response
base_wait, wait_time = calculate_retry_wait(base_wait, retry_strategy.max_sleep)
num_retries += 1
total_sleep += wait_time
time.sleep(wait_time)
class _DoNothingHash(object):
"""Do-nothing hash object.
Intended as a stand-in for ``hashlib.md5`` or a crc32c checksum
implementation in cases where it isn't necessary to compute the hash.
"""
def update(self, unused_chunk):
"""Do-nothing ``update`` method.
Intended to match the interface of ``hashlib.md5`` and other checksums.
Args:
unused_chunk (bytes): A chunk of data.
"""

View File

@@ -0,0 +1,976 @@
# Copyright 2017 Google Inc.
#
# 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.
"""Virtual bases classes for uploading media via Google APIs.
Supported here are:
* simple (media) uploads
* multipart uploads that contain both metadata and a small file as payload
* resumable uploads (with metadata as well)
"""
import http.client
import json
import os
import random
import sys
from google import _async_resumable_media
from google._async_resumable_media import _helpers
from google.resumable_media import _helpers as sync_helpers
from google.resumable_media import _upload as sync_upload
from google.resumable_media import common
from google.resumable_media._upload import (
_CONTENT_TYPE_HEADER,
_CONTENT_RANGE_TEMPLATE,
_RANGE_UNKNOWN_TEMPLATE,
_EMPTY_RANGE_TEMPLATE,
_BOUNDARY_FORMAT,
_MULTIPART_SEP,
_CRLF,
_MULTIPART_BEGIN,
_RELATED_HEADER,
_BYTES_RANGE_RE,
_STREAM_ERROR_TEMPLATE,
_POST,
_PUT,
_UPLOAD_CHECKSUM_MISMATCH_MESSAGE,
_UPLOAD_METADATA_NO_APPROPRIATE_CHECKSUM_MESSAGE,
)
class UploadBase(object):
"""Base class for upload helpers.
Defines core shared behavior across different upload types.
Args:
upload_url (str): The URL where the content will be uploaded.
headers (Optional[Mapping[str, str]]): Extra headers that should
be sent with the request, e.g. headers for encrypted data.
Attributes:
upload_url (str): The URL where the content will be uploaded.
"""
def __init__(self, upload_url, headers=None):
self.upload_url = upload_url
if headers is None:
headers = {}
self._headers = headers
self._finished = False
self._retry_strategy = common.RetryStrategy()
@property
def finished(self):
"""bool: Flag indicating if the upload has completed."""
return self._finished
def _process_response(self, response):
"""Process the response from an HTTP request.
This is everything that must be done after a request that doesn't
require network I/O (or other I/O). This is based on the `sans-I/O`_
philosophy.
Args:
response (object): The HTTP response object.
Raises:
~google.resumable_media.common.InvalidResponse: If the status
code is not 200.
.. _sans-I/O: https://sans-io.readthedocs.io/
"""
# Tombstone the current upload so it cannot be used again (in either
# failure or success).
self._finished = True
_helpers.require_status_code(response, (http.client.OK,), self._get_status_code)
@staticmethod
def _get_status_code(response):
"""Access the status code from an HTTP response.
Args:
response (object): The HTTP response object.
Raises:
NotImplementedError: Always, since virtual.
"""
raise NotImplementedError("This implementation is virtual.")
@staticmethod
def _get_headers(response):
"""Access the headers from an HTTP response.
Args:
response (object): The HTTP response object.
Raises:
NotImplementedError: Always, since virtual.
"""
raise NotImplementedError("This implementation is virtual.")
@staticmethod
def _get_body(response):
"""Access the response body from an HTTP response.
Args:
response (object): The HTTP response object.
Raises:
NotImplementedError: Always, since virtual.
"""
raise NotImplementedError("This implementation is virtual.")
class SimpleUpload(UploadBase):
"""Upload a resource to a Google API.
A **simple** media upload sends no metadata and completes the upload
in a single request.
Args:
upload_url (str): The URL where the content will be uploaded.
headers (Optional[Mapping[str, str]]): Extra headers that should
be sent with the request, e.g. headers for encrypted data.
Attributes:
upload_url (str): The URL where the content will be uploaded.
"""
def _prepare_request(self, data, content_type):
"""Prepare the contents of an HTTP request.
This is everything that must be done before a request that doesn't
require network I/O (or other I/O). This is based on the `sans-I/O`_
philosophy.
.. note:
This method will be used only once, so ``headers`` will be
mutated by having a new key added to it.
Args:
data (bytes): The resource content to be uploaded.
content_type (str): The content type for the request.
Returns:
Tuple[str, str, bytes, Mapping[str, str]]: The quadruple
* HTTP verb for the request (always POST)
* the URL for the request
* the body of the request
* headers for the request
Raises:
ValueError: If the current upload has already finished.
TypeError: If ``data`` isn't bytes.
.. _sans-I/O: https://sans-io.readthedocs.io/
"""
if self.finished:
raise ValueError("An upload can only be used once.")
if not isinstance(data, bytes):
raise TypeError("`data` must be bytes, received", type(data))
self._headers[_CONTENT_TYPE_HEADER] = content_type
return _POST, self.upload_url, data, self._headers
def transmit(self, transport, data, content_type, timeout=None):
"""Transmit the resource to be uploaded.
Args:
transport (object): An object which can make authenticated
requests.
data (bytes): The resource content to be uploaded.
content_type (str): The content type of the resource, e.g. a JPEG
image has content type ``image/jpeg``.
timeout (Optional[Union[float, aiohttp.ClientTimeout]]):
The number of seconds to wait for the server response.
Depending on the retry strategy, a request may be repeated
several times using the same timeout each time.
Can also be passed as an `aiohttp.ClientTimeout` object.
Raises:
NotImplementedError: Always, since virtual.
"""
raise NotImplementedError("This implementation is virtual.")
class MultipartUpload(UploadBase):
"""Upload a resource with metadata to a Google API.
A **multipart** upload sends both metadata and the resource in a single
(multipart) request.
Args:
upload_url (str): The URL where the content will be uploaded.
headers (Optional[Mapping[str, str]]): Extra headers that should
be sent with the request, e.g. headers for encrypted data.
checksum Optional([str]): The type of checksum to compute to verify
the integrity of the object. The request metadata will be amended
to include the computed value. Using this option will override a
manually-set checksum value. Supported values are "md5", "crc32c"
and None. The default is None.
Attributes:
upload_url (str): The URL where the content will be uploaded.
"""
def __init__(self, upload_url, headers=None, checksum=None):
super(MultipartUpload, self).__init__(upload_url, headers=headers)
self._checksum_type = checksum
def _prepare_request(self, data, metadata, content_type):
"""Prepare the contents of an HTTP request.
This is everything that must be done before a request that doesn't
require network I/O (or other I/O). This is based on the `sans-I/O`_
philosophy.
.. note:
This method will be used only once, so ``headers`` will be
mutated by having a new key added to it.
Args:
data (bytes): The resource content to be uploaded.
metadata (Mapping[str, str]): The resource metadata, such as an
ACL list.
content_type (str): The content type of the resource, e.g. a JPEG
image has content type ``image/jpeg``.
Returns:
Tuple[str, str, bytes, Mapping[str, str]]: The quadruple
* HTTP verb for the request (always POST)
* the URL for the request
* the body of the request
* headers for the request
Raises:
ValueError: If the current upload has already finished.
TypeError: If ``data`` isn't bytes.
.. _sans-I/O: https://sans-io.readthedocs.io/
"""
if self.finished:
raise ValueError("An upload can only be used once.")
if not isinstance(data, bytes):
raise TypeError("`data` must be bytes, received", type(data))
checksum_object = sync_helpers._get_checksum_object(self._checksum_type)
if checksum_object is not None:
checksum_object.update(data)
actual_checksum = sync_helpers.prepare_checksum_digest(
checksum_object.digest()
)
metadata_key = sync_helpers._get_metadata_key(self._checksum_type)
metadata[metadata_key] = actual_checksum
content, multipart_boundary = construct_multipart_request(
data, metadata, content_type
)
multipart_content_type = _RELATED_HEADER + multipart_boundary + b'"'
self._headers[_CONTENT_TYPE_HEADER] = multipart_content_type
return _POST, self.upload_url, content, self._headers
def transmit(self, transport, data, metadata, content_type, timeout=None):
"""Transmit the resource to be uploaded.
Args:
transport (object): An object which can make authenticated
requests.
data (bytes): The resource content to be uploaded.
metadata (Mapping[str, str]): The resource metadata, such as an
ACL list.
content_type (str): The content type of the resource, e.g. a JPEG
image has content type ``image/jpeg``.
timeout (Optional[Union[float, aiohttp.ClientTimeout]]):
The number of seconds to wait for the server response.
Depending on the retry strategy, a request may be repeated
several times using the same timeout each time.
Can also be passed as an `aiohttp.ClientTimeout` object.
Raises:
NotImplementedError: Always, since virtual.
"""
raise NotImplementedError("This implementation is virtual.")
class ResumableUpload(UploadBase, sync_upload.ResumableUpload):
"""Initiate and fulfill a resumable upload to a Google API.
A **resumable** upload sends an initial request with the resource metadata
and then gets assigned an upload ID / upload URL to send bytes to.
Using the upload URL, the upload is then done in chunks (determined by
the user) until all bytes have been uploaded.
Args:
upload_url (str): The URL where the resumable upload will be initiated.
chunk_size (int): The size of each chunk used to upload the resource.
headers (Optional[Mapping[str, str]]): Extra headers that should
be sent with the :meth:`initiate` request, e.g. headers for
encrypted data. These **will not** be sent with
:meth:`transmit_next_chunk` or :meth:`recover` requests.
checksum Optional([str]): The type of checksum to compute to verify
the integrity of the object. After the upload is complete, the
server-computed checksum of the resulting object will be read
and google.resumable_media.common.DataCorruption will be raised on
a mismatch. The corrupted file will not be deleted from the remote
host automatically. Supported values are "md5", "crc32c" and None.
The default is None.
Attributes:
upload_url (str): The URL where the content will be uploaded.
Raises:
ValueError: If ``chunk_size`` is not a multiple of
:data:`.UPLOAD_CHUNK_SIZE`.
"""
def __init__(self, upload_url, chunk_size, checksum=None, headers=None):
super(ResumableUpload, self).__init__(upload_url, headers=headers)
if chunk_size % _async_resumable_media.UPLOAD_CHUNK_SIZE != 0:
raise ValueError(
"{} KB must divide chunk size".format(
_async_resumable_media.UPLOAD_CHUNK_SIZE / 1024
)
)
self._chunk_size = chunk_size
self._stream = None
self._content_type = None
self._bytes_uploaded = 0
self._bytes_checksummed = 0
self._checksum_type = checksum
self._checksum_object = None
self._total_bytes = None
self._resumable_url = None
self._invalid = False
@property
def invalid(self):
"""bool: Indicates if the upload is in an invalid state.
This will occur if a call to :meth:`transmit_next_chunk` fails.
To recover from such a failure, call :meth:`recover`.
"""
return self._invalid
@property
def chunk_size(self):
"""int: The size of each chunk used to upload the resource."""
return self._chunk_size
@property
def resumable_url(self):
"""Optional[str]: The URL of the in-progress resumable upload."""
return self._resumable_url
@property
def bytes_uploaded(self):
"""int: Number of bytes that have been uploaded."""
return self._bytes_uploaded
@property
def total_bytes(self):
"""Optional[int]: The total number of bytes to be uploaded.
If this upload is initiated (via :meth:`initiate`) with
``stream_final=True``, this value will be populated based on the size
of the ``stream`` being uploaded. (By default ``stream_final=True``.)
If this upload is initiated with ``stream_final=False``,
:attr:`total_bytes` will be :data:`None` since it cannot be
determined from the stream.
"""
return self._total_bytes
def _prepare_initiate_request(
self, stream, metadata, content_type, total_bytes=None, stream_final=True
):
"""Prepare the contents of HTTP request to initiate upload.
This is everything that must be done before a request that doesn't
require network I/O (or other I/O). This is based on the `sans-I/O`_
philosophy.
Args:
stream (IO[bytes]): The stream (i.e. file-like object) that will
be uploaded. The stream **must** be at the beginning (i.e.
``stream.tell() == 0``).
metadata (Mapping[str, str]): The resource metadata, such as an
ACL list.
content_type (str): The content type of the resource, e.g. a JPEG
image has content type ``image/jpeg``.
total_bytes (Optional[int]): The total number of bytes to be
uploaded. If specified, the upload size **will not** be
determined from the stream (even if ``stream_final=True``).
stream_final (Optional[bool]): Indicates if the ``stream`` is
"final" (i.e. no more bytes will be added to it). In this case
we determine the upload size from the size of the stream. If
``total_bytes`` is passed, this argument will be ignored.
Returns:
Tuple[str, str, bytes, Mapping[str, str]]: The quadruple
* HTTP verb for the request (always POST)
* the URL for the request
* the body of the request
* headers for the request
Raises:
ValueError: If the current upload has already been initiated.
ValueError: If ``stream`` is not at the beginning.
.. _sans-I/O: https://sans-io.readthedocs.io/
"""
if self.resumable_url is not None:
raise ValueError("This upload has already been initiated.")
if stream.tell() != 0:
raise ValueError("Stream must be at beginning.")
self._stream = stream
self._content_type = content_type
headers = {
_CONTENT_TYPE_HEADER: "application/json; charset=UTF-8",
"x-upload-content-type": content_type,
}
# Set the total bytes if possible.
if total_bytes is not None:
self._total_bytes = total_bytes
elif stream_final:
self._total_bytes = get_total_bytes(stream)
# Add the total bytes to the headers if set.
if self._total_bytes is not None:
content_length = "{:d}".format(self._total_bytes)
headers["x-upload-content-length"] = content_length
headers.update(self._headers)
payload = json.dumps(metadata).encode("utf-8")
return _POST, self.upload_url, payload, headers
def _process_initiate_response(self, response):
"""Process the response from an HTTP request that initiated upload.
This is everything that must be done after a request that doesn't
require network I/O (or other I/O). This is based on the `sans-I/O`_
philosophy.
This method takes the URL from the ``Location`` header and stores it
for future use. Within that URL, we assume the ``upload_id`` query
parameter has been included, but we do not check.
Args:
response (object): The HTTP response object (need headers).
.. _sans-I/O: https://sans-io.readthedocs.io/
"""
_helpers.require_status_code(
response,
(http.client.OK,),
self._get_status_code,
callback=self._make_invalid,
)
self._resumable_url = _helpers.header_required(
response, "location", self._get_headers
)
def initiate(
self,
transport,
stream,
metadata,
content_type,
total_bytes=None,
stream_final=True,
timeout=None,
):
"""Initiate a resumable upload.
By default, this method assumes your ``stream`` is in a "final"
state ready to transmit. However, ``stream_final=False`` can be used
to indicate that the size of the resource is not known. This can happen
if bytes are being dynamically fed into ``stream``, e.g. if the stream
is attached to application logs.
If ``stream_final=False`` is used, :attr:`chunk_size` bytes will be
read from the stream every time :meth:`transmit_next_chunk` is called.
If one of those reads produces strictly fewer bites than the chunk
size, the upload will be concluded.
Args:
transport (object): An object which can make authenticated
requests.
stream (IO[bytes]): The stream (i.e. file-like object) that will
be uploaded. The stream **must** be at the beginning (i.e.
``stream.tell() == 0``).
metadata (Mapping[str, str]): The resource metadata, such as an
ACL list.
content_type (str): The content type of the resource, e.g. a JPEG
image has content type ``image/jpeg``.
total_bytes (Optional[int]): The total number of bytes to be
uploaded. If specified, the upload size **will not** be
determined from the stream (even if ``stream_final=True``).
stream_final (Optional[bool]): Indicates if the ``stream`` is
"final" (i.e. no more bytes will be added to it). In this case
we determine the upload size from the size of the stream. If
``total_bytes`` is passed, this argument will be ignored.
timeout (Optional[Union[float, aiohttp.ClientTimeout]]):
The number of seconds to wait for the server response.
Depending on the retry strategy, a request may be repeated
several times using the same timeout each time.
Can also be passed as an `aiohttp.ClientTimeout` object.
Raises:
NotImplementedError: Always, since virtual.
"""
raise NotImplementedError("This implementation is virtual.")
def _prepare_request(self):
"""Prepare the contents of HTTP request to upload a chunk.
This is everything that must be done before a request that doesn't
require network I/O. This is based on the `sans-I/O`_ philosophy.
For the time being, this **does require** some form of I/O to read
a chunk from ``stream`` (via :func:`get_next_chunk`). However, this
will (almost) certainly not be network I/O.
Returns:
Tuple[str, str, bytes, Mapping[str, str]]: The quadruple
* HTTP verb for the request (always PUT)
* the URL for the request
* the body of the request
* headers for the request
The headers **do not** incorporate the ``_headers`` on the
current instance.
Raises:
ValueError: If the current upload has finished.
ValueError: If the current upload is in an invalid state.
ValueError: If the current upload has not been initiated.
ValueError: If the location in the stream (i.e. ``stream.tell()``)
does not agree with ``bytes_uploaded``.
.. _sans-I/O: https://sans-io.readthedocs.io/
"""
if self.finished:
raise ValueError("Upload has finished.")
if self.invalid:
raise ValueError(
"Upload is in an invalid state. To recover call `recover()`."
)
if self.resumable_url is None:
raise ValueError(
"This upload has not been initiated. Please call "
"initiate() before beginning to transmit chunks."
)
start_byte, payload, content_range = get_next_chunk(
self._stream, self._chunk_size, self._total_bytes
)
if start_byte != self.bytes_uploaded:
msg = _STREAM_ERROR_TEMPLATE.format(start_byte, self.bytes_uploaded)
raise ValueError(msg)
self._update_checksum(start_byte, payload)
headers = {
_CONTENT_TYPE_HEADER: self._content_type,
_helpers.CONTENT_RANGE_HEADER: content_range,
}
return _PUT, self.resumable_url, payload, headers
def _make_invalid(self):
"""Simple setter for ``invalid``.
This is intended to be passed along as a callback to helpers that
raise an exception so they can mark this instance as invalid before
raising.
"""
self._invalid = True
async def _process_resumable_response(self, response, bytes_sent):
"""Process the response from an HTTP request.
This is everything that must be done after a request that doesn't
require network I/O (or other I/O). This is based on the `sans-I/O`_
philosophy.
Args:
response (object): The HTTP response object.
bytes_sent (int): The number of bytes sent in the request that
``response`` was returned for.
Raises:
~google.resumable_media.common.InvalidResponse: If the status
code is 308 and the ``range`` header is not of the form
``bytes 0-{end}``.
~google.resumable_media.common.InvalidResponse: If the status
code is not 200 or 308.
.. _sans-I/O: https://sans-io.readthedocs.io/
"""
status_code = _helpers.require_status_code(
response,
(http.client.OK, http.client.PERMANENT_REDIRECT),
self._get_status_code,
callback=self._make_invalid,
)
if status_code == http.client.OK:
# NOTE: We use the "local" information of ``bytes_sent`` to update
# ``bytes_uploaded``, but do not verify this against other
# state. However, there may be some other information:
#
# * a ``size`` key in JSON response body
# * the ``total_bytes`` attribute (if set)
# * ``stream.tell()`` (relying on fact that ``initiate()``
# requires stream to be at the beginning)
self._bytes_uploaded = self._bytes_uploaded + bytes_sent
# Tombstone the current upload so it cannot be used again.
self._finished = True
# Validate the checksum. This can raise an exception on failure.
await self._validate_checksum(response)
else:
bytes_range = _helpers.header_required(
response,
_helpers.RANGE_HEADER,
self._get_headers,
callback=self._make_invalid,
)
match = _BYTES_RANGE_RE.match(bytes_range)
if match is None:
self._make_invalid()
raise common.InvalidResponse(
response,
'Unexpected "range" header',
bytes_range,
'Expected to be of the form "bytes=0-{end}"',
)
self._bytes_uploaded = int(match.group("end_byte")) + 1
async def _validate_checksum(self, response):
"""Check the computed checksum, if any, against the response headers.
Args:
response (object): The HTTP response object.
Raises:
~google.resumable_media.common.DataCorruption: If the checksum
computed locally and the checksum reported by the remote host do
not match.
"""
if self._checksum_type is None:
return
metadata_key = sync_helpers._get_metadata_key(self._checksum_type)
metadata = await response.json()
remote_checksum = metadata.get(metadata_key)
if remote_checksum is None:
raise common.InvalidResponse(
response,
_UPLOAD_METADATA_NO_APPROPRIATE_CHECKSUM_MESSAGE.format(metadata_key),
self._get_headers(response),
)
local_checksum = sync_helpers.prepare_checksum_digest(
self._checksum_object.digest()
)
if local_checksum != remote_checksum:
raise common.DataCorruption(
response,
_UPLOAD_CHECKSUM_MISMATCH_MESSAGE.format(
self._checksum_type.upper(), local_checksum, remote_checksum
),
)
def transmit_next_chunk(self, transport, timeout=None):
"""Transmit the next chunk of the resource to be uploaded.
If the current upload was initiated with ``stream_final=False``,
this method will dynamically determine if the upload has completed.
The upload will be considered complete if the stream produces
fewer than :attr:`chunk_size` bytes when a chunk is read from it.
Args:
transport (object): An object which can make authenticated
requests.
timeout (Optional[Union[float, aiohttp.ClientTimeout]]):
The number of seconds to wait for the server response.
Depending on the retry strategy, a request may be repeated
several times using the same timeout each time.
Can also be passed as an `aiohttp.ClientTimeout` object.
Raises:
NotImplementedError: Always, since virtual.
"""
raise NotImplementedError("This implementation is virtual.")
def _prepare_recover_request(self):
"""Prepare the contents of HTTP request to recover from failure.
This is everything that must be done before a request that doesn't
require network I/O. This is based on the `sans-I/O`_ philosophy.
We assume that the :attr:`resumable_url` is set (i.e. the only way
the upload can end up :attr:`invalid` is if it has been initiated.
Returns:
Tuple[str, str, NoneType, Mapping[str, str]]: The quadruple
* HTTP verb for the request (always PUT)
* the URL for the request
* the body of the request (always :data:`None`)
* headers for the request
The headers **do not** incorporate the ``_headers`` on the
current instance.
Raises:
ValueError: If the current upload is not in an invalid state.
.. _sans-I/O: https://sans-io.readthedocs.io/
"""
if not self.invalid:
raise ValueError("Upload is not in invalid state, no need to recover.")
headers = {_helpers.CONTENT_RANGE_HEADER: "bytes */*"}
return _PUT, self.resumable_url, None, headers
def _process_recover_response(self, response):
"""Process the response from an HTTP request to recover from failure.
This is everything that must be done after a request that doesn't
require network I/O (or other I/O). This is based on the `sans-I/O`_
philosophy.
Args:
response (object): The HTTP response object.
Raises:
~google.resumable_media.common.InvalidResponse: If the status
code is not 308.
~google.resumable_media.common.InvalidResponse: If the status
code is 308 and the ``range`` header is not of the form
``bytes 0-{end}``.
.. _sans-I/O: https://sans-io.readthedocs.io/
"""
_helpers.require_status_code(
response,
(http.client.PERMANENT_REDIRECT,),
self._get_status_code,
)
headers = self._get_headers(response)
if _helpers.RANGE_HEADER in headers:
bytes_range = headers[_helpers.RANGE_HEADER]
match = _BYTES_RANGE_RE.match(bytes_range)
if match is None:
raise common.InvalidResponse(
response,
'Unexpected "range" header',
bytes_range,
'Expected to be of the form "bytes=0-{end}"',
)
self._bytes_uploaded = int(match.group("end_byte")) + 1
else:
# In this case, the upload has not "begun".
self._bytes_uploaded = 0
self._stream.seek(self._bytes_uploaded)
self._invalid = False
def recover(self, transport):
"""Recover from a failure.
This method should be used when a :class:`ResumableUpload` is in an
:attr:`~ResumableUpload.invalid` state due to a request failure.
This will verify the progress with the server and make sure the
current upload is in a valid state before :meth:`transmit_next_chunk`
can be used again.
Args:
transport (object): An object which can make authenticated
requests.
Raises:
NotImplementedError: Always, since virtual.
"""
raise NotImplementedError("This implementation is virtual.")
def get_boundary():
"""Get a random boundary for a multipart request.
Returns:
bytes: The boundary used to separate parts of a multipart request.
"""
random_int = random.randrange(sys.maxsize)
boundary = _BOUNDARY_FORMAT.format(random_int)
# NOTE: Neither % formatting nor .format() are available for byte strings
# in Python 3.4, so we must use unicode strings as templates.
return boundary.encode("utf-8")
def construct_multipart_request(data, metadata, content_type):
"""Construct a multipart request body.
Args:
data (bytes): The resource content (UTF-8 encoded as bytes)
to be uploaded.
metadata (Mapping[str, str]): The resource metadata, such as an
ACL list.
content_type (str): The content type of the resource, e.g. a JPEG
image has content type ``image/jpeg``.
Returns:
Tuple[bytes, bytes]: The multipart request body and the boundary used
between each part.
"""
multipart_boundary = get_boundary()
json_bytes = json.dumps(metadata).encode("utf-8")
content_type = content_type.encode("utf-8")
# Combine the two parts into a multipart payload.
# NOTE: We'd prefer a bytes template but are restricted by Python 3.4.
boundary_sep = _MULTIPART_SEP + multipart_boundary
content = (
boundary_sep
+ _MULTIPART_BEGIN
+ json_bytes
+ _CRLF
+ boundary_sep
+ _CRLF
+ b"content-type: "
+ content_type
+ _CRLF
+ _CRLF
+ data # Empty line between headers and body.
+ _CRLF
+ boundary_sep
+ _MULTIPART_SEP
)
return content, multipart_boundary
def get_total_bytes(stream):
"""Determine the total number of bytes in a stream.
Args:
stream (IO[bytes]): The stream (i.e. file-like object).
Returns:
int: The number of bytes.
"""
current_position = stream.tell()
# NOTE: ``.seek()`` **should** return the same value that ``.tell()``
# returns, but in Python 2, ``file`` objects do not.
stream.seek(0, os.SEEK_END)
end_position = stream.tell()
# Go back to the initial position.
stream.seek(current_position)
return end_position
def get_next_chunk(stream, chunk_size, total_bytes):
"""Get a chunk from an I/O stream.
The ``stream`` may have fewer bytes remaining than ``chunk_size``
so it may not always be the case that
``end_byte == start_byte + chunk_size - 1``.
Args:
stream (IO[bytes]): The stream (i.e. file-like object).
chunk_size (int): The size of the chunk to be read from the ``stream``.
total_bytes (Optional[int]): The (expected) total number of bytes
in the ``stream``.
Returns:
Tuple[int, bytes, str]: Triple of:
* the start byte index
* the content in between the start and end bytes (inclusive)
* content range header for the chunk (slice) that has been read
Raises:
ValueError: If ``total_bytes == 0`` but ``stream.read()`` yields
non-empty content.
ValueError: If there is no data left to consume. This corresponds
exactly to the case ``end_byte < start_byte``, which can only
occur if ``end_byte == start_byte - 1``.
"""
start_byte = stream.tell()
if total_bytes is not None and start_byte + chunk_size >= total_bytes > 0:
payload = stream.read(total_bytes - start_byte)
else:
payload = stream.read(chunk_size)
end_byte = stream.tell() - 1
num_bytes_read = len(payload)
if total_bytes is None:
if num_bytes_read < chunk_size:
# We now **KNOW** the total number of bytes.
total_bytes = end_byte + 1
elif total_bytes == 0:
# NOTE: We also expect ``start_byte == 0`` here but don't check
# because ``_prepare_initiate_request()`` requires the
# stream to be at the beginning.
if num_bytes_read != 0:
raise ValueError(
"Stream specified as empty, but produced non-empty content."
)
else:
if num_bytes_read == 0:
raise ValueError(
"Stream is already exhausted. There is no content remaining."
)
content_range = get_content_range(start_byte, end_byte, total_bytes)
return start_byte, payload, content_range
def get_content_range(start_byte, end_byte, total_bytes):
"""Convert start, end and total into content range header.
If ``total_bytes`` is not known, uses "bytes {start}-{end}/*".
If we are dealing with an empty range (i.e. ``end_byte < start_byte``)
then "bytes */{total}" is used.
This function **ASSUMES** that if the size is not known, the caller will
not also pass an empty range.
Args:
start_byte (int): The start (inclusive) of the byte range.
end_byte (int): The end (inclusive) of the byte range.
total_bytes (Optional[int]): The number of bytes in the byte
range (if known).
Returns:
str: The content range header.
"""
if total_bytes is None:
return _RANGE_UNKNOWN_TEMPLATE.format(start_byte, end_byte)
elif end_byte < start_byte:
return _EMPTY_RANGE_TEMPLATE.format(total_bytes)
else:
return _CONTENT_RANGE_TEMPLATE.format(start_byte, end_byte, total_bytes)

View File

@@ -0,0 +1,682 @@
# Copyright 2017 Google Inc.
#
# 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.
"""``requests`` utilities for Google Media Downloads and Resumable Uploads.
This sub-package assumes callers will use the `requests`_ library
as transport and `google-auth`_ for sending authenticated HTTP traffic
with ``requests``.
.. _requests: http://docs.python-requests.org/
.. _google-auth: https://google-auth.readthedocs.io/
====================
Authorized Transport
====================
To use ``google-auth`` and ``requests`` to create an authorized transport
that has read-only access to Google Cloud Storage (GCS):
.. testsetup:: get-credentials
import google.auth
import google.auth.credentials as creds_mod
import mock
def mock_default(scopes=None):
credentials = mock.Mock(spec=creds_mod.Credentials)
return credentials, 'mock-project'
# Patch the ``default`` function on the module.
original_default = google.auth.default
google.auth.default = mock_default
.. doctest:: get-credentials
>>> import google.auth
>>> import google.auth.transport.requests as tr_requests
>>>
>>> ro_scope = 'https://www.googleapis.com/auth/devstorage.read_only'
>>> credentials, _ = google.auth.default(scopes=(ro_scope,))
>>> transport = tr_requests.AuthorizedSession(credentials)
>>> transport
<google.auth.transport.requests.AuthorizedSession object at 0x...>
.. testcleanup:: get-credentials
# Put back the correct ``default`` function on the module.
google.auth.default = original_default
================
Simple Downloads
================
To download an object from Google Cloud Storage, construct the media URL
for the GCS object and download it with an authorized transport that has
access to the resource:
.. testsetup:: basic-download
import mock
import requests
import http.client
bucket = 'bucket-foo'
blob_name = 'file.txt'
fake_response = requests.Response()
fake_response.status_code = int(http.client.OK)
fake_response.headers['Content-Length'] = '1364156'
fake_content = mock.MagicMock(spec=['__len__'])
fake_content.__len__.return_value = 1364156
fake_response._content = fake_content
get_method = mock.Mock(return_value=fake_response, spec=[])
transport = mock.Mock(request=get_method, spec=['request'])
.. doctest:: basic-download
>>> from google.resumable_media.requests import Download
>>>
>>> url_template = (
... 'https://www.googleapis.com/download/storage/v1/b/'
... '{bucket}/o/{blob_name}?alt=media')
>>> media_url = url_template.format(
... bucket=bucket, blob_name=blob_name)
>>>
>>> download = Download(media_url)
>>> response = download.consume(transport)
>>> download.finished
True
>>> response
<Response [200]>
>>> response.headers['Content-Length']
'1364156'
>>> len(response.content)
1364156
To download only a portion of the bytes in the object,
specify ``start`` and ``end`` byte positions (both optional):
.. testsetup:: basic-download-with-slice
import mock
import requests
import http.client
from google.resumable_media.requests import Download
media_url = 'http://test.invalid'
start = 4096
end = 8191
slice_size = end - start + 1
fake_response = requests.Response()
fake_response.status_code = int(http.client.PARTIAL_CONTENT)
fake_response.headers['Content-Length'] = '{:d}'.format(slice_size)
content_range = 'bytes {:d}-{:d}/1364156'.format(start, end)
fake_response.headers['Content-Range'] = content_range
fake_content = mock.MagicMock(spec=['__len__'])
fake_content.__len__.return_value = slice_size
fake_response._content = fake_content
get_method = mock.Mock(return_value=fake_response, spec=[])
transport = mock.Mock(request=get_method, spec=['request'])
.. doctest:: basic-download-with-slice
>>> download = Download(media_url, start=4096, end=8191)
>>> response = download.consume(transport)
>>> download.finished
True
>>> response
<Response [206]>
>>> response.headers['Content-Length']
'4096'
>>> response.headers['Content-Range']
'bytes 4096-8191/1364156'
>>> len(response.content)
4096
=================
Chunked Downloads
=================
For very large objects or objects of unknown size, it may make more sense
to download the object in chunks rather than all at once. This can be done
to avoid dropped connections with a poor internet connection or can allow
multiple chunks to be downloaded in parallel to speed up the total
download.
A :class:`.ChunkedDownload` uses the same media URL and authorized
transport that a basic :class:`.Download` would use, but also
requires a chunk size and a write-able byte ``stream``. The chunk size is used
to determine how much of the resouce to consume with each request and the
stream is to allow the resource to be written out (e.g. to disk) without
having to fit in memory all at once.
.. testsetup:: chunked-download
import io
import mock
import requests
import http.client
media_url = 'http://test.invalid'
fifty_mb = 50 * 1024 * 1024
one_gb = 1024 * 1024 * 1024
fake_response = requests.Response()
fake_response.status_code = int(http.client.PARTIAL_CONTENT)
fake_response.headers['Content-Length'] = '{:d}'.format(fifty_mb)
content_range = 'bytes 0-{:d}/{:d}'.format(fifty_mb - 1, one_gb)
fake_response.headers['Content-Range'] = content_range
fake_content_begin = b'The beginning of the chunk...'
fake_content = fake_content_begin + b'1' * (fifty_mb - 29)
fake_response._content = fake_content
get_method = mock.Mock(return_value=fake_response, spec=[])
transport = mock.Mock(request=get_method, spec=['request'])
.. doctest:: chunked-download
>>> from google.resumable_media.requests import ChunkedDownload
>>>
>>> chunk_size = 50 * 1024 * 1024 # 50MB
>>> stream = io.BytesIO()
>>> download = ChunkedDownload(
... media_url, chunk_size, stream)
>>> # Check the state of the download before starting.
>>> download.bytes_downloaded
0
>>> download.total_bytes is None
True
>>> response = download.consume_next_chunk(transport)
>>> # Check the state of the download after consuming one chunk.
>>> download.finished
False
>>> download.bytes_downloaded # chunk_size
52428800
>>> download.total_bytes # 1GB
1073741824
>>> response
<Response [206]>
>>> response.headers['Content-Length']
'52428800'
>>> response.headers['Content-Range']
'bytes 0-52428799/1073741824'
>>> len(response.content) == chunk_size
True
>>> stream.seek(0)
0
>>> stream.read(29)
b'The beginning of the chunk...'
The download will change it's ``finished`` status to :data:`True`
once the final chunk is consumed. In some cases, the final chunk may
not be the same size as the other chunks:
.. testsetup:: chunked-download-end
import mock
import requests
import http.client
from google.resumable_media.requests import ChunkedDownload
media_url = 'http://test.invalid'
fifty_mb = 50 * 1024 * 1024
one_gb = 1024 * 1024 * 1024
stream = mock.Mock(spec=['write'])
download = ChunkedDownload(media_url, fifty_mb, stream)
download._bytes_downloaded = 20 * fifty_mb
download._total_bytes = one_gb
fake_response = requests.Response()
fake_response.status_code = int(http.client.PARTIAL_CONTENT)
slice_size = one_gb - 20 * fifty_mb
fake_response.headers['Content-Length'] = '{:d}'.format(slice_size)
content_range = 'bytes {:d}-{:d}/{:d}'.format(
20 * fifty_mb, one_gb - 1, one_gb)
fake_response.headers['Content-Range'] = content_range
fake_content = mock.MagicMock(spec=['__len__'])
fake_content.__len__.return_value = slice_size
fake_response._content = fake_content
get_method = mock.Mock(return_value=fake_response, spec=[])
transport = mock.Mock(request=get_method, spec=['request'])
.. doctest:: chunked-download-end
>>> # The state of the download in progress.
>>> download.finished
False
>>> download.bytes_downloaded # 20 chunks at 50MB
1048576000
>>> download.total_bytes # 1GB
1073741824
>>> response = download.consume_next_chunk(transport)
>>> # The state of the download after consuming the final chunk.
>>> download.finished
True
>>> download.bytes_downloaded == download.total_bytes
True
>>> response
<Response [206]>
>>> response.headers['Content-Length']
'25165824'
>>> response.headers['Content-Range']
'bytes 1048576000-1073741823/1073741824'
>>> len(response.content) < download.chunk_size
True
In addition, a :class:`.ChunkedDownload` can also take optional
``start`` and ``end`` byte positions.
Usually, no checksum is returned with a chunked download. Even if one is returned,
it is not validated. If you need to validate the checksum, you can do so
by buffering the chunks and validating the checksum against the completed download.
==============
Simple Uploads
==============
Among the three supported upload classes, the simplest is
:class:`.SimpleUpload`. A simple upload should be used when the resource
being uploaded is small and when there is no metadata (other than the name)
associated with the resource.
.. testsetup:: simple-upload
import json
import mock
import requests
import http.client
bucket = 'some-bucket'
blob_name = 'file.txt'
fake_response = requests.Response()
fake_response.status_code = int(http.client.OK)
payload = {
'bucket': bucket,
'contentType': 'text/plain',
'md5Hash': 'M0XLEsX9/sMdiI+4pB4CAQ==',
'name': blob_name,
'size': '27',
}
fake_response._content = json.dumps(payload).encode('utf-8')
post_method = mock.Mock(return_value=fake_response, spec=[])
transport = mock.Mock(request=post_method, spec=['request'])
.. doctest:: simple-upload
:options: +NORMALIZE_WHITESPACE
>>> from google.resumable_media.requests import SimpleUpload
>>>
>>> url_template = (
... 'https://www.googleapis.com/upload/storage/v1/b/{bucket}/o?'
... 'uploadType=media&'
... 'name={blob_name}')
>>> upload_url = url_template.format(
... bucket=bucket, blob_name=blob_name)
>>>
>>> upload = SimpleUpload(upload_url)
>>> data = b'Some not too large content.'
>>> content_type = 'text/plain'
>>> response = upload.transmit(transport, data, content_type)
>>> upload.finished
True
>>> response
<Response [200]>
>>> json_response = response.json()
>>> json_response['bucket'] == bucket
True
>>> json_response['name'] == blob_name
True
>>> json_response['contentType'] == content_type
True
>>> json_response['md5Hash']
'M0XLEsX9/sMdiI+4pB4CAQ=='
>>> int(json_response['size']) == len(data)
True
In the rare case that an upload fails, an :exc:`.InvalidResponse`
will be raised:
.. testsetup:: simple-upload-fail
import time
import mock
import requests
import http.client
from google import resumable_media
from google.resumable_media import _helpers
from google.resumable_media.requests import SimpleUpload as constructor
upload_url = 'http://test.invalid'
data = b'Some not too large content.'
content_type = 'text/plain'
fake_response = requests.Response()
fake_response.status_code = int(http.client.SERVICE_UNAVAILABLE)
post_method = mock.Mock(return_value=fake_response, spec=[])
transport = mock.Mock(request=post_method, spec=['request'])
time_sleep = time.sleep
def dont_sleep(seconds):
raise RuntimeError('No sleep', seconds)
def SimpleUpload(*args, **kwargs):
upload = constructor(*args, **kwargs)
# Mock the cumulative sleep to avoid retries (and `time.sleep()`).
upload._retry_strategy = resumable_media.RetryStrategy(
max_cumulative_retry=-1.0)
return upload
time.sleep = dont_sleep
.. doctest:: simple-upload-fail
:options: +NORMALIZE_WHITESPACE
>>> upload = SimpleUpload(upload_url)
>>> error = None
>>> try:
... upload.transmit(transport, data, content_type)
... except resumable_media.InvalidResponse as caught_exc:
... error = caught_exc
...
>>> error
InvalidResponse('Request failed with status code', 503,
'Expected one of', <HTTPStatus.OK: 200>)
>>> error.response
<Response [503]>
>>>
>>> upload.finished
True
.. testcleanup:: simple-upload-fail
# Put back the correct ``sleep`` function on the ``time`` module.
time.sleep = time_sleep
Even in the case of failure, we see that the upload is
:attr:`~.SimpleUpload.finished`, i.e. it cannot be re-used.
=================
Multipart Uploads
=================
After the simple upload, the :class:`.MultipartUpload` can be used to
achieve essentially the same task. However, a multipart upload allows some
metadata about the resource to be sent along as well. (This is the "multi":
we send a first part with the metadata and a second part with the actual
bytes in the resource.)
Usage is similar to the simple upload, but :meth:`~.MultipartUpload.transmit`
accepts an extra required argument: ``metadata``.
.. testsetup:: multipart-upload
import json
import mock
import requests
import http.client
bucket = 'some-bucket'
blob_name = 'file.txt'
data = b'Some not too large content.'
content_type = 'text/plain'
fake_response = requests.Response()
fake_response.status_code = int(http.client.OK)
payload = {
'bucket': bucket,
'name': blob_name,
'metadata': {'color': 'grurple'},
}
fake_response._content = json.dumps(payload).encode('utf-8')
post_method = mock.Mock(return_value=fake_response, spec=[])
transport = mock.Mock(request=post_method, spec=['request'])
.. doctest:: multipart-upload
>>> from google.resumable_media.requests import MultipartUpload
>>>
>>> url_template = (
... 'https://www.googleapis.com/upload/storage/v1/b/{bucket}/o?'
... 'uploadType=multipart')
>>> upload_url = url_template.format(bucket=bucket)
>>>
>>> upload = MultipartUpload(upload_url)
>>> metadata = {
... 'name': blob_name,
... 'metadata': {
... 'color': 'grurple',
... },
... }
>>> response = upload.transmit(transport, data, metadata, content_type)
>>> upload.finished
True
>>> response
<Response [200]>
>>> json_response = response.json()
>>> json_response['bucket'] == bucket
True
>>> json_response['name'] == blob_name
True
>>> json_response['metadata'] == metadata['metadata']
True
As with the simple upload, in the case of failure an :exc:`.InvalidResponse`
is raised, enclosing the :attr:`~.InvalidResponse.response` that caused
the failure and the ``upload`` object cannot be re-used after a failure.
=================
Resumable Uploads
=================
A :class:`.ResumableUpload` deviates from the other two upload classes:
it transmits a resource over the course of multiple requests. This
is intended to be used in cases where:
* the size of the resource is not known (i.e. it is generated on the fly)
* requests must be short-lived
* the client has request **size** limitations
* the resource is too large to fit into memory
In general, a resource should be sent in a **single** request to avoid
latency and reduce QPS. See `GCS best practices`_ for more things to
consider when using a resumable upload.
.. _GCS best practices: https://cloud.google.com/storage/docs/\
best-practices#uploading
After creating a :class:`.ResumableUpload` instance, a
**resumable upload session** must be initiated to let the server know that
a series of chunked upload requests will be coming and to obtain an
``upload_id`` for the session. In contrast to the other two upload classes,
:meth:`~.ResumableUpload.initiate` takes a byte ``stream`` as input rather
than raw bytes as ``data``. This can be a file object, a :class:`~io.BytesIO`
object or any other stream implementing the same interface.
.. testsetup:: resumable-initiate
import io
import mock
import requests
import http.client
bucket = 'some-bucket'
blob_name = 'file.txt'
data = b'Some resumable bytes.'
content_type = 'text/plain'
fake_response = requests.Response()
fake_response.status_code = int(http.client.OK)
fake_response._content = b''
upload_id = 'ABCdef189XY_super_serious'
resumable_url_template = (
'https://www.googleapis.com/upload/storage/v1/b/{bucket}'
'/o?uploadType=resumable&upload_id={upload_id}')
resumable_url = resumable_url_template.format(
bucket=bucket, upload_id=upload_id)
fake_response.headers['location'] = resumable_url
fake_response.headers['x-guploader-uploadid'] = upload_id
post_method = mock.Mock(return_value=fake_response, spec=[])
transport = mock.Mock(request=post_method, spec=['request'])
.. doctest:: resumable-initiate
>>> from google.resumable_media.requests import ResumableUpload
>>>
>>> url_template = (
... 'https://www.googleapis.com/upload/storage/v1/b/{bucket}/o?'
... 'uploadType=resumable')
>>> upload_url = url_template.format(bucket=bucket)
>>>
>>> chunk_size = 1024 * 1024 # 1MB
>>> upload = ResumableUpload(upload_url, chunk_size)
>>> stream = io.BytesIO(data)
>>> # The upload doesn't know how "big" it is until seeing a stream.
>>> upload.total_bytes is None
True
>>> metadata = {'name': blob_name}
>>> response = upload.initiate(transport, stream, metadata, content_type)
>>> response
<Response [200]>
>>> upload.resumable_url == response.headers['Location']
True
>>> upload.total_bytes == len(data)
True
>>> upload_id = response.headers['X-GUploader-UploadID']
>>> upload_id
'ABCdef189XY_super_serious'
>>> upload.resumable_url == upload_url + '&upload_id=' + upload_id
True
Once a :class:`.ResumableUpload` has been initiated, the resource is
transmitted in chunks until completion:
.. testsetup:: resumable-transmit
import io
import json
import mock
import requests
import http.client
from google import resumable_media
import google.resumable_media.requests.upload as upload_mod
data = b'01234567891'
stream = io.BytesIO(data)
# Create an "already initiated" upload.
upload_url = 'http://test.invalid'
chunk_size = 256 * 1024 # 256KB
upload = upload_mod.ResumableUpload(upload_url, chunk_size)
upload._resumable_url = 'http://test.invalid?upload_id=mocked'
upload._stream = stream
upload._content_type = 'text/plain'
upload._total_bytes = len(data)
# After-the-fact update the chunk size so that len(data)
# is split into three.
upload._chunk_size = 4
# Make three fake responses.
fake_response0 = requests.Response()
fake_response0.status_code = http.client.PERMANENT_REDIRECT
fake_response0.headers['range'] = 'bytes=0-3'
fake_response1 = requests.Response()
fake_response1.status_code = http.client.PERMANENT_REDIRECT
fake_response1.headers['range'] = 'bytes=0-7'
fake_response2 = requests.Response()
fake_response2.status_code = int(http.client.OK)
bucket = 'some-bucket'
blob_name = 'file.txt'
payload = {
'bucket': bucket,
'name': blob_name,
'size': '{:d}'.format(len(data)),
}
fake_response2._content = json.dumps(payload).encode('utf-8')
# Use the fake responses to mock a transport.
responses = [fake_response0, fake_response1, fake_response2]
put_method = mock.Mock(side_effect=responses, spec=[])
transport = mock.Mock(request=put_method, spec=['request'])
.. doctest:: resumable-transmit
>>> response0 = upload.transmit_next_chunk(transport)
>>> response0
<Response [308]>
>>> upload.finished
False
>>> upload.bytes_uploaded == upload.chunk_size
True
>>>
>>> response1 = upload.transmit_next_chunk(transport)
>>> response1
<Response [308]>
>>> upload.finished
False
>>> upload.bytes_uploaded == 2 * upload.chunk_size
True
>>>
>>> response2 = upload.transmit_next_chunk(transport)
>>> response2
<Response [200]>
>>> upload.finished
True
>>> upload.bytes_uploaded == upload.total_bytes
True
>>> json_response = response2.json()
>>> json_response['bucket'] == bucket
True
>>> json_response['name'] == blob_name
True
"""
from google._async_resumable_media.requests.download import ChunkedDownload
from google._async_resumable_media.requests.download import Download
from google._async_resumable_media.requests.upload import MultipartUpload
from google._async_resumable_media.requests.download import RawChunkedDownload
from google._async_resumable_media.requests.download import RawDownload
from google._async_resumable_media.requests.upload import ResumableUpload
from google._async_resumable_media.requests.upload import SimpleUpload
__all__ = [
"ChunkedDownload",
"Download",
"MultipartUpload",
"RawChunkedDownload",
"RawDownload",
"ResumableUpload",
"SimpleUpload",
]

View File

@@ -0,0 +1,155 @@
# Copyright 2017 Google Inc.
#
# 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.
"""Shared utilities used by both downloads and uploads.
This utilities are explicitly catered to ``requests``-like transports.
"""
import functools
from google._async_resumable_media import _helpers
from google.resumable_media import common
from google.auth.transport import _aiohttp_requests as aiohttp_requests # type: ignore
import aiohttp # type: ignore
_DEFAULT_RETRY_STRATEGY = common.RetryStrategy()
_SINGLE_GET_CHUNK_SIZE = 8192
# The number of seconds to wait to establish a connection
# (connect() call on socket). Avoid setting this to a multiple of 3 to not
# Align with TCP Retransmission timing. (typically 2.5-3s)
_DEFAULT_CONNECT_TIMEOUT = 61
# The number of seconds to wait between bytes sent from the server.
_DEFAULT_READ_TIMEOUT = 60
_DEFAULT_TIMEOUT = aiohttp.ClientTimeout(
connect=_DEFAULT_CONNECT_TIMEOUT, sock_read=_DEFAULT_READ_TIMEOUT
)
class RequestsMixin(object):
"""Mix-in class implementing ``requests``-specific behavior.
These are methods that are more general purpose, with implementations
specific to the types defined in ``requests``.
"""
@staticmethod
def _get_status_code(response):
"""Access the status code from an HTTP response.
Args:
response (~requests.Response): The HTTP response object.
Returns:
int: The status code.
"""
return response.status
@staticmethod
def _get_headers(response):
"""Access the headers from an HTTP response.
Args:
response (~requests.Response): The HTTP response object.
Returns:
~requests.structures.CaseInsensitiveDict: The header mapping (keys
are case-insensitive).
"""
# For Async testing,`_headers` is modified instead of headers
# access via the internal field.
return response._headers
@staticmethod
async def _get_body(response):
"""Access the response body from an HTTP response.
Args:
response (~requests.Response): The HTTP response object.
Returns:
bytes: The body of the ``response``.
"""
wrapped_response = aiohttp_requests._CombinedResponse(response)
content = await wrapped_response.data.read()
return content
class RawRequestsMixin(RequestsMixin):
@staticmethod
async def _get_body(response):
"""Access the response body from an HTTP response.
Args:
response (~requests.Response): The HTTP response object.
Returns:
bytes: The body of the ``response``.
"""
wrapped_response = aiohttp_requests._CombinedResponse(response)
content = await wrapped_response.raw_content()
return content
async def http_request(
transport,
method,
url,
data=None,
headers=None,
retry_strategy=_DEFAULT_RETRY_STRATEGY,
**transport_kwargs
):
"""Make an HTTP request.
Args:
transport (~requests.Session): A ``requests`` object which can make
authenticated requests via a ``request()`` method. This method
must accept an HTTP method, an upload URL, a ``data`` keyword
argument and a ``headers`` keyword argument.
method (str): The HTTP method for the request.
url (str): The URL for the request.
data (Optional[bytes]): The body of the request.
headers (Mapping[str, str]): The headers for the request (``transport``
may also add additional headers).
retry_strategy (~google.resumable_media.common.RetryStrategy): The
strategy to use if the request fails and must be retried.
transport_kwargs (Dict[str, str]): Extra keyword arguments to be
passed along to ``transport.request``.
Returns:
~requests.Response: The return value of ``transport.request()``.
"""
# NOTE(asyncio/aiohttp): Sync versions use a tuple for two timeouts,
# default connect timeout and read timeout. Since async requests only
# accepts a single value, this is using the connect timeout. This logic
# diverges from the sync implementation.
if "timeout" not in transport_kwargs:
timeout = _DEFAULT_TIMEOUT
transport_kwargs["timeout"] = timeout
func = functools.partial(
transport.request, method, url, data=data, headers=headers, **transport_kwargs
)
resp = await _helpers.wait_and_retry(
func, RequestsMixin._get_status_code, retry_strategy
)
return resp

View File

@@ -0,0 +1,465 @@
# Copyright 2017 Google Inc.
#
# 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.
"""Support for downloading media from Google APIs."""
import urllib3.response # type: ignore
import http
from google._async_resumable_media import _download
from google._async_resumable_media import _helpers
from google._async_resumable_media.requests import _request_helpers
from google.resumable_media import common
from google.resumable_media import _helpers as sync_helpers
from google.resumable_media.requests import download
_CHECKSUM_MISMATCH = download._CHECKSUM_MISMATCH
class Download(_request_helpers.RequestsMixin, _download.Download):
"""Helper to manage downloading a resource from a Google API.
"Slices" of the resource can be retrieved by specifying a range
with ``start`` and / or ``end``. However, in typical usage, neither
``start`` nor ``end`` is expected to be provided.
Args:
media_url (str): The URL containing the media to be downloaded.
stream (IO[bytes]): A write-able stream (i.e. file-like object) that
the downloaded resource can be written to.
start (int): The first byte in a range to be downloaded. If not
provided, but ``end`` is provided, will download from the
beginning to ``end`` of the media.
end (int): The last byte in a range to be downloaded. If not
provided, but ``start`` is provided, will download from the
``start`` to the end of the media.
headers (Optional[Mapping[str, str]]): Extra headers that should
be sent with the request, e.g. headers for encrypted data.
checksum Optional([str]): The type of checksum to compute to verify
the integrity of the object. The response headers must contain
a checksum of the requested type. If the headers lack an
appropriate checksum (for instance in the case of transcoded or
ranged downloads where the remote service does not know the
correct checksum) an INFO-level log will be emitted. Supported
values are "md5", "crc32c" and None. The default is "md5".
Attributes:
media_url (str): The URL containing the media to be downloaded.
start (Optional[int]): The first byte in a range to be downloaded.
end (Optional[int]): The last byte in a range to be downloaded.
"""
async def _write_to_stream(self, response):
"""Write response body to a write-able stream.
.. note:
This method assumes that the ``_stream`` attribute is set on the
current download.
Args:
response (~requests.Response): The HTTP response object.
Raises:
~google.resumable_media.common.DataCorruption: If the download's
checksum doesn't agree with server-computed checksum.
"""
# `_get_expected_checksum()` may return None even if a checksum was
# requested, in which case it will emit an info log _MISSING_CHECKSUM.
# If an invalid checksum type is specified, this will raise ValueError.
expected_checksum, checksum_object = sync_helpers._get_expected_checksum(
response, self._get_headers, self.media_url, checksum_type=self.checksum
)
local_checksum_object = _add_decoder(response, checksum_object)
async for chunk in response.content.iter_chunked(
_request_helpers._SINGLE_GET_CHUNK_SIZE
):
self._stream.write(chunk)
local_checksum_object.update(chunk)
# Don't validate the checksum for partial responses.
if (
expected_checksum is not None
and response.status != http.client.PARTIAL_CONTENT
):
actual_checksum = sync_helpers.prepare_checksum_digest(
checksum_object.digest()
)
if actual_checksum != expected_checksum:
msg = _CHECKSUM_MISMATCH.format(
self.media_url,
expected_checksum,
actual_checksum,
checksum_type=self.checksum.upper(),
)
raise common.DataCorruption(response, msg)
async def consume(self, transport, timeout=_request_helpers._DEFAULT_TIMEOUT):
"""Consume the resource to be downloaded.
If a ``stream`` is attached to this download, then the downloaded
resource will be written to the stream.
Args:
transport (~requests.Session): A ``requests`` object which can
make authenticated requests.
timeout (Optional[Union[float, aiohttp.ClientTimeout]]):
The number of seconds to wait for the server response.
Depending on the retry strategy, a request may be repeated
several times using the same timeout each time.
Can also be passed as an `aiohttp.ClientTimeout` object.
Returns:
~requests.Response: The HTTP response returned by ``transport``.
Raises:
~google.resumable_media.common.DataCorruption: If the download's
checksum doesn't agree with server-computed checksum.
ValueError: If the current :class:`Download` has already
finished.
"""
method, url, payload, headers = self._prepare_request()
# NOTE: We assume "payload is None" but pass it along anyway.
request_kwargs = {
"data": payload,
"headers": headers,
"retry_strategy": self._retry_strategy,
"timeout": timeout,
}
if self._stream is not None:
request_kwargs["stream"] = True
result = await _request_helpers.http_request(
transport, method, url, **request_kwargs
)
self._process_response(result)
if self._stream is not None:
await self._write_to_stream(result)
return result
class RawDownload(_request_helpers.RawRequestsMixin, _download.Download):
"""Helper to manage downloading a raw resource from a Google API.
"Slices" of the resource can be retrieved by specifying a range
with ``start`` and / or ``end``. However, in typical usage, neither
``start`` nor ``end`` is expected to be provided.
Args:
media_url (str): The URL containing the media to be downloaded.
stream (IO[bytes]): A write-able stream (i.e. file-like object) that
the downloaded resource can be written to.
start (int): The first byte in a range to be downloaded. If not
provided, but ``end`` is provided, will download from the
beginning to ``end`` of the media.
end (int): The last byte in a range to be downloaded. If not
provided, but ``start`` is provided, will download from the
``start`` to the end of the media.
headers (Optional[Mapping[str, str]]): Extra headers that should
be sent with the request, e.g. headers for encrypted data.
checksum Optional([str]): The type of checksum to compute to verify
the integrity of the object. The response headers must contain
a checksum of the requested type. If the headers lack an
appropriate checksum (for instance in the case of transcoded or
ranged downloads where the remote service does not know the
correct checksum) an INFO-level log will be emitted. Supported
values are "md5", "crc32c" and None. The default is "md5".
Attributes:
media_url (str): The URL containing the media to be downloaded.
start (Optional[int]): The first byte in a range to be downloaded.
end (Optional[int]): The last byte in a range to be downloaded.
"""
async def _write_to_stream(self, response):
"""Write response body to a write-able stream.
.. note:
This method assumes that the ``_stream`` attribute is set on the
current download.
Args:
response (~requests.Response): The HTTP response object.
Raises:
~google.resumable_media.common.DataCorruption: If the download's
checksum doesn't agree with server-computed checksum.
"""
# `_get_expected_checksum()` may return None even if a checksum was
# requested, in which case it will emit an info log _MISSING_CHECKSUM.
# If an invalid checksum type is specified, this will raise ValueError.
expected_checksum, checksum_object = sync_helpers._get_expected_checksum(
response, self._get_headers, self.media_url, checksum_type=self.checksum
)
async for chunk in response.content.iter_chunked(
_request_helpers._SINGLE_GET_CHUNK_SIZE
):
self._stream.write(chunk)
checksum_object.update(chunk)
# Don't validate the checksum for partial responses.
if (
expected_checksum is not None
and response.status != http.client.PARTIAL_CONTENT
):
actual_checksum = sync_helpers.prepare_checksum_digest(
checksum_object.digest()
)
if actual_checksum != expected_checksum:
msg = _CHECKSUM_MISMATCH.format(
self.media_url,
expected_checksum,
actual_checksum,
checksum_type=self.checksum.upper(),
)
raise common.DataCorruption(response, msg)
async def consume(self, transport, timeout=_request_helpers._DEFAULT_TIMEOUT):
"""Consume the resource to be downloaded.
If a ``stream`` is attached to this download, then the downloaded
resource will be written to the stream.
Args:
transport (~requests.Session): A ``requests`` object which can
make authenticated requests.
timeout (Optional[Union[float, Tuple[float, float]]]):
The number of seconds to wait for the server response.
Depending on the retry strategy, a request may be repeated
several times using the same timeout each time.
Can also be passed as a tuple (connect_timeout, read_timeout).
See :meth:`requests.Session.request` documentation for details.
Returns:
~requests.Response: The HTTP response returned by ``transport``.
Raises:
~google.resumable_media.common.DataCorruption: If the download's
checksum doesn't agree with server-computed checksum.
ValueError: If the current :class:`Download` has already
finished.
"""
method, url, payload, headers = self._prepare_request()
# NOTE: We assume "payload is None" but pass it along anyway.
result = await _request_helpers.http_request(
transport,
method,
url,
data=payload,
headers=headers,
retry_strategy=self._retry_strategy,
)
self._process_response(result)
if self._stream is not None:
await self._write_to_stream(result)
return result
class ChunkedDownload(_request_helpers.RequestsMixin, _download.ChunkedDownload):
"""Download a resource in chunks from a Google API.
Args:
media_url (str): The URL containing the media to be downloaded.
chunk_size (int): The number of bytes to be retrieved in each
request.
stream (IO[bytes]): A write-able stream (i.e. file-like object) that
will be used to concatenate chunks of the resource as they are
downloaded.
start (int): The first byte in a range to be downloaded. If not
provided, defaults to ``0``.
end (int): The last byte in a range to be downloaded. If not
provided, will download to the end of the media.
headers (Optional[Mapping[str, str]]): Extra headers that should
be sent with each request, e.g. headers for data encryption
key headers.
Attributes:
media_url (str): The URL containing the media to be downloaded.
start (Optional[int]): The first byte in a range to be downloaded.
end (Optional[int]): The last byte in a range to be downloaded.
chunk_size (int): The number of bytes to be retrieved in each request.
Raises:
ValueError: If ``start`` is negative.
"""
async def consume_next_chunk(
self, transport, timeout=_request_helpers._DEFAULT_TIMEOUT
):
"""
Consume the next chunk of the resource to be downloaded.
Args:
transport (~requests.Session): A ``requests`` object which can
make authenticated requests.
timeout (Optional[Union[float, aiohttp.ClientTimeout]]):
The number of seconds to wait for the server response.
Depending on the retry strategy, a request may be repeated
several times using the same timeout each time.
Can also be passed as an `aiohttp.ClientTimeout` object.
Returns:
~requests.Response: The HTTP response returned by ``transport``.
Raises:
ValueError: If the current download has finished.
"""
method, url, payload, headers = self._prepare_request()
# NOTE: We assume "payload is None" but pass it along anyway.
result = await _request_helpers.http_request(
transport,
method,
url,
data=payload,
headers=headers,
retry_strategy=self._retry_strategy,
timeout=timeout,
)
await self._process_response(result)
return result
class RawChunkedDownload(_request_helpers.RawRequestsMixin, _download.ChunkedDownload):
"""Download a raw resource in chunks from a Google API.
Args:
media_url (str): The URL containing the media to be downloaded.
chunk_size (int): The number of bytes to be retrieved in each
request.
stream (IO[bytes]): A write-able stream (i.e. file-like object) that
will be used to concatenate chunks of the resource as they are
downloaded.
start (int): The first byte in a range to be downloaded. If not
provided, defaults to ``0``.
end (int): The last byte in a range to be downloaded. If not
provided, will download to the end of the media.
headers (Optional[Mapping[str, str]]): Extra headers that should
be sent with each request, e.g. headers for data encryption
key headers.
Attributes:
media_url (str): The URL containing the media to be downloaded.
start (Optional[int]): The first byte in a range to be downloaded.
end (Optional[int]): The last byte in a range to be downloaded.
chunk_size (int): The number of bytes to be retrieved in each request.
Raises:
ValueError: If ``start`` is negative.
"""
async def consume_next_chunk(
self, transport, timeout=_request_helpers._DEFAULT_TIMEOUT
):
"""Consume the next chunk of the resource to be downloaded.
Args:
transport (~requests.Session): A ``requests`` object which can
make authenticated requests.
timeout (Optional[Union[float, aiohttp.ClientTimeout]]):
The number of seconds to wait for the server response.
Depending on the retry strategy, a request may be repeated
several times using the same timeout each time.
Can also be passed as an `aiohttp.ClientTimeout` object.
Returns:
~requests.Response: The HTTP response returned by ``transport``.
Raises:
ValueError: If the current download has finished.
"""
method, url, payload, headers = self._prepare_request()
# NOTE: We assume "payload is None" but pass it along anyway.
result = await _request_helpers.http_request(
transport,
method,
url,
data=payload,
headers=headers,
retry_strategy=self._retry_strategy,
timeout=timeout,
)
await self._process_response(result)
return result
def _add_decoder(response_raw, checksum):
"""Patch the ``_decoder`` on a ``urllib3`` response.
This is so that we can intercept the compressed bytes before they are
decoded.
Only patches if the content encoding is ``gzip``.
Args:
response_raw (urllib3.response.HTTPResponse): The raw response for
an HTTP request.
checksum (object):
A checksum which will be updated with compressed bytes.
Returns:
object: Either the original ``checksum`` if ``_decoder`` is not
patched, or a ``_DoNothingHash`` if the decoder is patched, since the
caller will no longer need to hash to decoded bytes.
"""
encoding = response_raw.headers.get("content-encoding", "").lower()
if encoding != "gzip":
return checksum
response_raw._decoder = _GzipDecoder(checksum)
return _helpers._DoNothingHash()
class _GzipDecoder(urllib3.response.GzipDecoder):
"""Custom subclass of ``urllib3`` decoder for ``gzip``-ed bytes.
Allows a checksum function to see the compressed bytes before they are
decoded. This way the checksum of the compressed value can be computed.
Args:
checksum (object):
A checksum which will be updated with compressed bytes.
"""
def __init__(self, checksum):
super(_GzipDecoder, self).__init__()
self._checksum = checksum
def decompress(self, data):
"""Decompress the bytes.
Args:
data (bytes): The compressed bytes to be decompressed.
Returns:
bytes: The decompressed bytes from ``data``.
"""
self._checksum.update(data)
return super(_GzipDecoder, self).decompress(data)

View File

@@ -0,0 +1,515 @@
# Copyright 2017 Google Inc.
#
# 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.
"""Support for resumable uploads.
Also supported here are simple (media) uploads and multipart
uploads that contain both metadata and a small file as payload.
"""
from google._async_resumable_media import _upload
from google._async_resumable_media.requests import _request_helpers
class SimpleUpload(_request_helpers.RequestsMixin, _upload.SimpleUpload):
"""Upload a resource to a Google API.
A **simple** media upload sends no metadata and completes the upload
in a single request.
Args:
upload_url (str): The URL where the content will be uploaded.
headers (Optional[Mapping[str, str]]): Extra headers that should
be sent with the request, e.g. headers for encrypted data.
Attributes:
upload_url (str): The URL where the content will be uploaded.
"""
async def transmit(
self,
transport,
data,
content_type,
timeout=_request_helpers._DEFAULT_TIMEOUT,
):
"""Transmit the resource to be uploaded.
Args:
transport (~requests.Session): A ``requests`` object which can
make authenticated requests.
data (bytes): The resource content to be uploaded.
content_type (str): The content type of the resource, e.g. a JPEG
image has content type ``image/jpeg``.
timeout (Optional[Union[float, aiohttp.ClientTimeout]]):
The number of seconds to wait for the server response.
Depending on the retry strategy, a request may be repeated
several times using the same timeout each time.
Can also be passed as an `aiohttp.ClientTimeout` object.
Returns:
~requests.Response: The HTTP response returned by ``transport``.
"""
method, url, payload, headers = self._prepare_request(data, content_type)
response = await _request_helpers.http_request(
transport,
method,
url,
data=payload,
headers=headers,
retry_strategy=self._retry_strategy,
timeout=timeout,
)
self._process_response(response)
return response
class MultipartUpload(_request_helpers.RequestsMixin, _upload.MultipartUpload):
"""Upload a resource with metadata to a Google API.
A **multipart** upload sends both metadata and the resource in a single
(multipart) request.
Args:
upload_url (str): The URL where the content will be uploaded.
headers (Optional[Mapping[str, str]]): Extra headers that should
be sent with the request, e.g. headers for encrypted data.
checksum Optional([str]): The type of checksum to compute to verify
the integrity of the object. The request metadata will be amended
to include the computed value. Using this option will override a
manually-set checksum value. Supported values are "md5",
"crc32c" and None. The default is None.
Attributes:
upload_url (str): The URL where the content will be uploaded.
"""
async def transmit(
self,
transport,
data,
metadata,
content_type,
timeout=_request_helpers._DEFAULT_TIMEOUT,
):
"""Transmit the resource to be uploaded.
Args:
transport (~requests.Session): A ``requests`` object which can
make authenticated requests.
data (bytes): The resource content to be uploaded.
metadata (Mapping[str, str]): The resource metadata, such as an
ACL list.
content_type (str): The content type of the resource, e.g. a JPEG
image has content type ``image/jpeg``.
timeout (Optional[Union[float, aiohttp.ClientTimeout]]):
The number of seconds to wait for the server response.
Depending on the retry strategy, a request may be repeated
several times using the same timeout each time.
Can also be passed as an `aiohttp.ClientTimeout` object.
Returns:
~requests.Response: The HTTP response returned by ``transport``.
"""
method, url, payload, headers = self._prepare_request(
data, metadata, content_type
)
response = await _request_helpers.http_request(
transport,
method,
url,
data=payload,
headers=headers,
retry_strategy=self._retry_strategy,
timeout=timeout,
)
self._process_response(response)
return response
class ResumableUpload(_request_helpers.RequestsMixin, _upload.ResumableUpload):
"""Initiate and fulfill a resumable upload to a Google API.
A **resumable** upload sends an initial request with the resource metadata
and then gets assigned an upload ID / upload URL to send bytes to.
Using the upload URL, the upload is then done in chunks (determined by
the user) until all bytes have been uploaded.
When constructing a resumable upload, only the resumable upload URL and
the chunk size are required:
.. testsetup:: resumable-constructor
bucket = 'bucket-foo'
.. doctest:: resumable-constructor
>>> from google.resumable_media.requests import ResumableUpload
>>>
>>> url_template = (
... 'https://www.googleapis.com/upload/storage/v1/b/{bucket}/o?'
... 'uploadType=resumable')
>>> upload_url = url_template.format(bucket=bucket)
>>>
>>> chunk_size = 3 * 1024 * 1024 # 3MB
>>> upload = ResumableUpload(upload_url, chunk_size)
When initiating an upload (via :meth:`initiate`), the caller is expected
to pass the resource being uploaded as a file-like ``stream``. If the size
of the resource is explicitly known, it can be passed in directly:
.. testsetup:: resumable-explicit-size
import os
import tempfile
import mock
import requests
import http.client
from google.resumable_media.requests import ResumableUpload
upload_url = 'http://test.invalid'
chunk_size = 3 * 1024 * 1024 # 3MB
upload = ResumableUpload(upload_url, chunk_size)
file_desc, filename = tempfile.mkstemp()
os.close(file_desc)
data = b'some bytes!'
with open(filename, 'wb') as file_obj:
file_obj.write(data)
fake_response = requests.Response()
fake_response.status_code = int(http.client.OK)
fake_response._content = b''
resumable_url = 'http://test.invalid?upload_id=7up'
fake_response.headers['location'] = resumable_url
post_method = mock.Mock(return_value=fake_response, spec=[])
transport = mock.Mock(request=post_method, spec=['request'])
.. doctest:: resumable-explicit-size
>>> import os
>>>
>>> upload.total_bytes is None
True
>>>
>>> stream = open(filename, 'rb')
>>> total_bytes = os.path.getsize(filename)
>>> metadata = {'name': filename}
>>> response = upload.initiate(
... transport, stream, metadata, 'text/plain',
... total_bytes=total_bytes)
>>> response
<Response [200]>
>>>
>>> upload.total_bytes == total_bytes
True
.. testcleanup:: resumable-explicit-size
os.remove(filename)
If the stream is in a "final" state (i.e. it won't have any more bytes
written to it), the total number of bytes can be determined implicitly
from the ``stream`` itself:
.. testsetup:: resumable-implicit-size
import io
import mock
import requests
import http.client
from google.resumable_media.requests import ResumableUpload
upload_url = 'http://test.invalid'
chunk_size = 3 * 1024 * 1024 # 3MB
upload = ResumableUpload(upload_url, chunk_size)
fake_response = requests.Response()
fake_response.status_code = int(http.client.OK)
fake_response._content = b''
resumable_url = 'http://test.invalid?upload_id=7up'
fake_response.headers['location'] = resumable_url
post_method = mock.Mock(return_value=fake_response, spec=[])
transport = mock.Mock(request=post_method, spec=['request'])
data = b'some MOAR bytes!'
metadata = {'name': 'some-file.jpg'}
content_type = 'image/jpeg'
.. doctest:: resumable-implicit-size
>>> stream = io.BytesIO(data)
>>> response = upload.initiate(
... transport, stream, metadata, content_type)
>>>
>>> upload.total_bytes == len(data)
True
If the size of the resource is **unknown** when the upload is initiated,
the ``stream_final`` argument can be used. This might occur if the
resource is being dynamically created on the client (e.g. application
logs). To use this argument:
.. testsetup:: resumable-unknown-size
import io
import mock
import requests
import http.client
from google.resumable_media.requests import ResumableUpload
upload_url = 'http://test.invalid'
chunk_size = 3 * 1024 * 1024 # 3MB
upload = ResumableUpload(upload_url, chunk_size)
fake_response = requests.Response()
fake_response.status_code = int(http.client.OK)
fake_response._content = b''
resumable_url = 'http://test.invalid?upload_id=7up'
fake_response.headers['location'] = resumable_url
post_method = mock.Mock(return_value=fake_response, spec=[])
transport = mock.Mock(request=post_method, spec=['request'])
metadata = {'name': 'some-file.jpg'}
content_type = 'application/octet-stream'
stream = io.BytesIO(b'data')
.. doctest:: resumable-unknown-size
>>> response = upload.initiate(
... transport, stream, metadata, content_type,
... stream_final=False)
>>>
>>> upload.total_bytes is None
True
Args:
upload_url (str): The URL where the resumable upload will be initiated.
chunk_size (int): The size of each chunk used to upload the resource.
headers (Optional[Mapping[str, str]]): Extra headers that should
be sent with the :meth:`initiate` request, e.g. headers for
encrypted data. These **will not** be sent with
:meth:`transmit_next_chunk` or :meth:`recover` requests.
checksum Optional([str]): The type of checksum to compute to verify
the integrity of the object. After the upload is complete, the
server-computed checksum of the resulting object will be checked
and google.resumable_media.common.DataCorruption will be raised on
a mismatch. The corrupted file will not be deleted from the remote
host automatically. Supported values are "md5", "crc32c" and None.
The default is None.
Attributes:
upload_url (str): The URL where the content will be uploaded.
Raises:
ValueError: If ``chunk_size`` is not a multiple of
:data:`.UPLOAD_CHUNK_SIZE`.
"""
async def initiate(
self,
transport,
stream,
metadata,
content_type,
total_bytes=None,
stream_final=True,
timeout=_request_helpers._DEFAULT_TIMEOUT,
):
"""Initiate a resumable upload.
By default, this method assumes your ``stream`` is in a "final"
state ready to transmit. However, ``stream_final=False`` can be used
to indicate that the size of the resource is not known. This can happen
if bytes are being dynamically fed into ``stream``, e.g. if the stream
is attached to application logs.
If ``stream_final=False`` is used, :attr:`chunk_size` bytes will be
read from the stream every time :meth:`transmit_next_chunk` is called.
If one of those reads produces strictly fewer bites than the chunk
size, the upload will be concluded.
Args:
transport (~requests.Session): A ``requests`` object which can
make authenticated requests.
stream (IO[bytes]): The stream (i.e. file-like object) that will
be uploaded. The stream **must** be at the beginning (i.e.
``stream.tell() == 0``).
metadata (Mapping[str, str]): The resource metadata, such as an
ACL list.
content_type (str): The content type of the resource, e.g. a JPEG
image has content type ``image/jpeg``.
total_bytes (Optional[int]): The total number of bytes to be
uploaded. If specified, the upload size **will not** be
determined from the stream (even if ``stream_final=True``).
stream_final (Optional[bool]): Indicates if the ``stream`` is
"final" (i.e. no more bytes will be added to it). In this case
we determine the upload size from the size of the stream. If
``total_bytes`` is passed, this argument will be ignored.
timeout (Optional[Union[float, aiohttp.ClientTimeout]]):
The number of seconds to wait for the server response.
Depending on the retry strategy, a request may be repeated
several times using the same timeout each time.
Can also be passed as an `aiohttp.ClientTimeout` object.
Returns:
~requests.Response: The HTTP response returned by ``transport``.
"""
method, url, payload, headers = self._prepare_initiate_request(
stream,
metadata,
content_type,
total_bytes=total_bytes,
stream_final=stream_final,
)
response = await _request_helpers.http_request(
transport,
method,
url,
data=payload,
headers=headers,
retry_strategy=self._retry_strategy,
timeout=timeout,
)
self._process_initiate_response(response)
return response
async def transmit_next_chunk(
self, transport, timeout=_request_helpers._DEFAULT_TIMEOUT
):
"""Transmit the next chunk of the resource to be uploaded.
If the current upload was initiated with ``stream_final=False``,
this method will dynamically determine if the upload has completed.
The upload will be considered complete if the stream produces
fewer than :attr:`chunk_size` bytes when a chunk is read from it.
In the case of failure, an exception is thrown that preserves the
failed response:
.. testsetup:: bad-response
import io
import mock
import requests
import http.client
from google import resumable_media
import google.resumable_media.requests.upload as upload_mod
transport = mock.Mock(spec=['request'])
fake_response = requests.Response()
fake_response.status_code = int(http.client.BAD_REQUEST)
transport.request.return_value = fake_response
upload_url = 'http://test.invalid'
upload = upload_mod.ResumableUpload(
upload_url, resumable_media.UPLOAD_CHUNK_SIZE)
# Fake that the upload has been initiate()-d
data = b'data is here'
upload._stream = io.BytesIO(data)
upload._total_bytes = len(data)
upload._resumable_url = 'http://test.invalid?upload_id=nope'
.. doctest:: bad-response
:options: +NORMALIZE_WHITESPACE
>>> error = None
>>> try:
... upload.transmit_next_chunk(transport)
... except resumable_media.InvalidResponse as caught_exc:
... error = caught_exc
...
>>> error
InvalidResponse('Request failed with status code', 400,
'Expected one of', <HTTPStatus.OK: 200>, 308)
>>> error.response
<Response [400]>
Args:
transport (~requests.Session): A ``requests`` object which can
make authenticated requests.
timeout (Optional[Union[float, aiohttp.ClientTimeout]]):
The number of seconds to wait for the server response.
Depending on the retry strategy, a request may be repeated
several times using the same timeout each time.
Can also be passed as an `aiohttp.ClientTimeout` object.
Returns:
~requests.Response: The HTTP response returned by ``transport``.
Raises:
~google.resumable_media.common.InvalidResponse: If the status
code is not 200 or 308.
~google.resumable_media.common.DataCorruption: If this is the final
chunk, a checksum validation was requested, and the checksum
does not match or is not available.
"""
method, url, payload, headers = self._prepare_request()
response = await _request_helpers.http_request(
transport,
method,
url,
data=payload,
headers=headers,
retry_strategy=self._retry_strategy,
timeout=timeout,
)
await self._process_resumable_response(response, len(payload))
return response
async def recover(self, transport):
"""Recover from a failure.
This method should be used when a :class:`ResumableUpload` is in an
:attr:`~ResumableUpload.invalid` state due to a request failure.
This will verify the progress with the server and make sure the
current upload is in a valid state before :meth:`transmit_next_chunk`
can be used again.
Args:
transport (~requests.Session): A ``requests`` object which can
make authenticated requests.
Returns:
~requests.Response: The HTTP response returned by ``transport``.
"""
method, url, payload, headers = self._prepare_recover_request()
# NOTE: We assume "payload is None" but pass it along anyway.
response = await _request_helpers.http_request(
transport,
method,
url,
data=payload,
headers=headers,
retry_strategy=self._retry_strategy,
)
self._process_recover_response(response)
return response

View File

@@ -0,0 +1,31 @@
// Copyright 2025 Google LLC
//
// 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.
syntax = "proto3";
package google.api;
import "google/api/http.proto";
import "google/protobuf/descriptor.proto";
option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
option java_multiple_files = true;
option java_outer_classname = "AnnotationsProto";
option java_package = "com.google.api";
option objc_class_prefix = "GAPI";
extend google.protobuf.MethodOptions {
// See `HttpRule`.
HttpRule http = 72295728;
}

View File

@@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
# Copyright 2025 Google LLC
#
# 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.
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: google/api/annotations.proto
# Protobuf Python Version: 4.25.3
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
from google.api import http_pb2 as google_dot_api_dot_http__pb2
from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
b'\n\x1cgoogle/api/annotations.proto\x12\ngoogle.api\x1a\x15google/api/http.proto\x1a google/protobuf/descriptor.proto:E\n\x04http\x12\x1e.google.protobuf.MethodOptions\x18\xb0\xca\xbc" \x01(\x0b\x32\x14.google.api.HttpRuleBn\n\x0e\x63om.google.apiB\x10\x41nnotationsProtoP\x01ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\xa2\x02\x04GAPIb\x06proto3'
)
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(
DESCRIPTOR, "google.api.annotations_pb2", _globals
)
if _descriptor._USE_C_DESCRIPTORS == False:
_globals["DESCRIPTOR"]._options = None
_globals[
"DESCRIPTOR"
]._serialized_options = b"\n\016com.google.apiB\020AnnotationsProtoP\001ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\242\002\004GAPI"
# @@protoc_insertion_point(module_scope)

View File

@@ -0,0 +1,23 @@
# Copyright 2025 Google LLC
#
# 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 typing import ClassVar as _ClassVar
from google.api import http_pb2 as _http_pb2
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pb2 as _descriptor_pb2
DESCRIPTOR: _descriptor.FileDescriptor
HTTP_FIELD_NUMBER: _ClassVar[int]
http: _descriptor.FieldDescriptor

View File

@@ -0,0 +1,237 @@
// Copyright 2025 Google LLC
//
// 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.
syntax = "proto3";
package google.api;
option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig";
option java_multiple_files = true;
option java_outer_classname = "AuthProto";
option java_package = "com.google.api";
option objc_class_prefix = "GAPI";
// `Authentication` defines the authentication configuration for API methods
// provided by an API service.
//
// Example:
//
// name: calendar.googleapis.com
// authentication:
// providers:
// - id: google_calendar_auth
// jwks_uri: https://www.googleapis.com/oauth2/v1/certs
// issuer: https://securetoken.google.com
// rules:
// - selector: "*"
// requirements:
// provider_id: google_calendar_auth
// - selector: google.calendar.Delegate
// oauth:
// canonical_scopes: https://www.googleapis.com/auth/calendar.read
message Authentication {
// A list of authentication rules that apply to individual API methods.
//
// **NOTE:** All service configuration rules follow "last one wins" order.
repeated AuthenticationRule rules = 3;
// Defines a set of authentication providers that a service supports.
repeated AuthProvider providers = 4;
}
// Authentication rules for the service.
//
// By default, if a method has any authentication requirements, every request
// must include a valid credential matching one of the requirements.
// It's an error to include more than one kind of credential in a single
// request.
//
// If a method doesn't have any auth requirements, request credentials will be
// ignored.
message AuthenticationRule {
// Selects the methods to which this rule applies.
//
// Refer to [selector][google.api.DocumentationRule.selector] for syntax
// details.
string selector = 1;
// The requirements for OAuth credentials.
OAuthRequirements oauth = 2;
// If true, the service accepts API keys without any other credential.
// This flag only applies to HTTP and gRPC requests.
bool allow_without_credential = 5;
// Requirements for additional authentication providers.
repeated AuthRequirement requirements = 7;
}
// Specifies a location to extract JWT from an API request.
message JwtLocation {
oneof in {
// Specifies HTTP header name to extract JWT token.
string header = 1;
// Specifies URL query parameter name to extract JWT token.
string query = 2;
// Specifies cookie name to extract JWT token.
string cookie = 4;
}
// The value prefix. The value format is "value_prefix{token}"
// Only applies to "in" header type. Must be empty for "in" query type.
// If not empty, the header value has to match (case sensitive) this prefix.
// If not matched, JWT will not be extracted. If matched, JWT will be
// extracted after the prefix is removed.
//
// For example, for "Authorization: Bearer {JWT}",
// value_prefix="Bearer " with a space at the end.
string value_prefix = 3;
}
// Configuration for an authentication provider, including support for
// [JSON Web Token
// (JWT)](https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32).
message AuthProvider {
// The unique identifier of the auth provider. It will be referred to by
// `AuthRequirement.provider_id`.
//
// Example: "bookstore_auth".
string id = 1;
// Identifies the principal that issued the JWT. See
// https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32#section-4.1.1
// Usually a URL or an email address.
//
// Example: https://securetoken.google.com
// Example: 1234567-compute@developer.gserviceaccount.com
string issuer = 2;
// URL of the provider's public key set to validate signature of the JWT. See
// [OpenID
// Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata).
// Optional if the key set document:
// - can be retrieved from
// [OpenID
// Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html)
// of the issuer.
// - can be inferred from the email domain of the issuer (e.g. a Google
// service account).
//
// Example: https://www.googleapis.com/oauth2/v1/certs
string jwks_uri = 3;
// The list of JWT
// [audiences](https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32#section-4.1.3).
// that are allowed to access. A JWT containing any of these audiences will
// be accepted. When this setting is absent, JWTs with audiences:
// - "https://[service.name]/[google.protobuf.Api.name]"
// - "https://[service.name]/"
// will be accepted.
// For example, if no audiences are in the setting, LibraryService API will
// accept JWTs with the following audiences:
// -
// https://library-example.googleapis.com/google.example.library.v1.LibraryService
// - https://library-example.googleapis.com/
//
// Example:
//
// audiences: bookstore_android.apps.googleusercontent.com,
// bookstore_web.apps.googleusercontent.com
string audiences = 4;
// Redirect URL if JWT token is required but not present or is expired.
// Implement authorizationUrl of securityDefinitions in OpenAPI spec.
string authorization_url = 5;
// Defines the locations to extract the JWT. For now it is only used by the
// Cloud Endpoints to store the OpenAPI extension [x-google-jwt-locations]
// (https://cloud.google.com/endpoints/docs/openapi/openapi-extensions#x-google-jwt-locations)
//
// JWT locations can be one of HTTP headers, URL query parameters or
// cookies. The rule is that the first match wins.
//
// If not specified, default to use following 3 locations:
// 1) Authorization: Bearer
// 2) x-goog-iap-jwt-assertion
// 3) access_token query parameter
//
// Default locations can be specified as followings:
// jwt_locations:
// - header: Authorization
// value_prefix: "Bearer "
// - header: x-goog-iap-jwt-assertion
// - query: access_token
repeated JwtLocation jwt_locations = 6;
}
// OAuth scopes are a way to define data and permissions on data. For example,
// there are scopes defined for "Read-only access to Google Calendar" and
// "Access to Cloud Platform". Users can consent to a scope for an application,
// giving it permission to access that data on their behalf.
//
// OAuth scope specifications should be fairly coarse grained; a user will need
// to see and understand the text description of what your scope means.
//
// In most cases: use one or at most two OAuth scopes for an entire family of
// products. If your product has multiple APIs, you should probably be sharing
// the OAuth scope across all of those APIs.
//
// When you need finer grained OAuth consent screens: talk with your product
// management about how developers will use them in practice.
//
// Please note that even though each of the canonical scopes is enough for a
// request to be accepted and passed to the backend, a request can still fail
// due to the backend requiring additional scopes or permissions.
message OAuthRequirements {
// The list of publicly documented OAuth scopes that are allowed access. An
// OAuth token containing any of these scopes will be accepted.
//
// Example:
//
// canonical_scopes: https://www.googleapis.com/auth/calendar,
// https://www.googleapis.com/auth/calendar.read
string canonical_scopes = 1;
}
// User-defined authentication requirements, including support for
// [JSON Web Token
// (JWT)](https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32).
message AuthRequirement {
// [id][google.api.AuthProvider.id] from authentication provider.
//
// Example:
//
// provider_id: bookstore_auth
string provider_id = 1;
// NOTE: This will be deprecated soon, once AuthProvider.audiences is
// implemented and accepted in all the runtime components.
//
// The list of JWT
// [audiences](https://tools.ietf.org/html/draft-ietf-oauth-json-web-token-32#section-4.1.3).
// that are allowed to access. A JWT containing any of these audiences will
// be accepted. When this setting is absent, only JWTs with audience
// "https://[Service_name][google.api.Service.name]/[API_name][google.protobuf.Api.name]"
// will be accepted. For example, if no audiences are in the setting,
// LibraryService API will only accept JWTs with the following audience
// "https://library-example.googleapis.com/google.example.library.v1.LibraryService".
//
// Example:
//
// audiences: bookstore_android.apps.googleusercontent.com,
// bookstore_web.apps.googleusercontent.com
string audiences = 2;
}

View File

@@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
# Copyright 2025 Google LLC
#
# 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.
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: google/api/auth.proto
# Protobuf Python Version: 4.25.3
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
b'\n\x15google/api/auth.proto\x12\ngoogle.api"l\n\x0e\x41uthentication\x12-\n\x05rules\x18\x03 \x03(\x0b\x32\x1e.google.api.AuthenticationRule\x12+\n\tproviders\x18\x04 \x03(\x0b\x32\x18.google.api.AuthProvider"\xa9\x01\n\x12\x41uthenticationRule\x12\x10\n\x08selector\x18\x01 \x01(\t\x12,\n\x05oauth\x18\x02 \x01(\x0b\x32\x1d.google.api.OAuthRequirements\x12 \n\x18\x61llow_without_credential\x18\x05 \x01(\x08\x12\x31\n\x0crequirements\x18\x07 \x03(\x0b\x32\x1b.google.api.AuthRequirement"^\n\x0bJwtLocation\x12\x10\n\x06header\x18\x01 \x01(\tH\x00\x12\x0f\n\x05query\x18\x02 \x01(\tH\x00\x12\x10\n\x06\x63ookie\x18\x04 \x01(\tH\x00\x12\x14\n\x0cvalue_prefix\x18\x03 \x01(\tB\x04\n\x02in"\x9a\x01\n\x0c\x41uthProvider\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0e\n\x06issuer\x18\x02 \x01(\t\x12\x10\n\x08jwks_uri\x18\x03 \x01(\t\x12\x11\n\taudiences\x18\x04 \x01(\t\x12\x19\n\x11\x61uthorization_url\x18\x05 \x01(\t\x12.\n\rjwt_locations\x18\x06 \x03(\x0b\x32\x17.google.api.JwtLocation"-\n\x11OAuthRequirements\x12\x18\n\x10\x63\x61nonical_scopes\x18\x01 \x01(\t"9\n\x0f\x41uthRequirement\x12\x13\n\x0bprovider_id\x18\x01 \x01(\t\x12\x11\n\taudiences\x18\x02 \x01(\tBk\n\x0e\x63om.google.apiB\tAuthProtoP\x01ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\xa2\x02\x04GAPIb\x06proto3'
)
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.api.auth_pb2", _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
_globals["DESCRIPTOR"]._options = None
_globals[
"DESCRIPTOR"
]._serialized_options = b"\n\016com.google.apiB\tAuthProtoP\001ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\242\002\004GAPI"
_globals["_AUTHENTICATION"]._serialized_start = 37
_globals["_AUTHENTICATION"]._serialized_end = 145
_globals["_AUTHENTICATIONRULE"]._serialized_start = 148
_globals["_AUTHENTICATIONRULE"]._serialized_end = 317
_globals["_JWTLOCATION"]._serialized_start = 319
_globals["_JWTLOCATION"]._serialized_end = 413
_globals["_AUTHPROVIDER"]._serialized_start = 416
_globals["_AUTHPROVIDER"]._serialized_end = 570
_globals["_OAUTHREQUIREMENTS"]._serialized_start = 572
_globals["_OAUTHREQUIREMENTS"]._serialized_end = 617
_globals["_AUTHREQUIREMENT"]._serialized_start = 619
_globals["_AUTHREQUIREMENT"]._serialized_end = 676
# @@protoc_insertion_point(module_scope)

View File

@@ -0,0 +1,120 @@
# Copyright 2025 Google LLC
#
# 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 typing import ClassVar as _ClassVar
from typing import Iterable as _Iterable
from typing import Mapping as _Mapping
from typing import Optional as _Optional
from typing import Union as _Union
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf.internal import containers as _containers
DESCRIPTOR: _descriptor.FileDescriptor
class Authentication(_message.Message):
__slots__ = ("rules", "providers")
RULES_FIELD_NUMBER: _ClassVar[int]
PROVIDERS_FIELD_NUMBER: _ClassVar[int]
rules: _containers.RepeatedCompositeFieldContainer[AuthenticationRule]
providers: _containers.RepeatedCompositeFieldContainer[AuthProvider]
def __init__(
self,
rules: _Optional[_Iterable[_Union[AuthenticationRule, _Mapping]]] = ...,
providers: _Optional[_Iterable[_Union[AuthProvider, _Mapping]]] = ...,
) -> None: ...
class AuthenticationRule(_message.Message):
__slots__ = ("selector", "oauth", "allow_without_credential", "requirements")
SELECTOR_FIELD_NUMBER: _ClassVar[int]
OAUTH_FIELD_NUMBER: _ClassVar[int]
ALLOW_WITHOUT_CREDENTIAL_FIELD_NUMBER: _ClassVar[int]
REQUIREMENTS_FIELD_NUMBER: _ClassVar[int]
selector: str
oauth: OAuthRequirements
allow_without_credential: bool
requirements: _containers.RepeatedCompositeFieldContainer[AuthRequirement]
def __init__(
self,
selector: _Optional[str] = ...,
oauth: _Optional[_Union[OAuthRequirements, _Mapping]] = ...,
allow_without_credential: bool = ...,
requirements: _Optional[_Iterable[_Union[AuthRequirement, _Mapping]]] = ...,
) -> None: ...
class JwtLocation(_message.Message):
__slots__ = ("header", "query", "cookie", "value_prefix")
HEADER_FIELD_NUMBER: _ClassVar[int]
QUERY_FIELD_NUMBER: _ClassVar[int]
COOKIE_FIELD_NUMBER: _ClassVar[int]
VALUE_PREFIX_FIELD_NUMBER: _ClassVar[int]
header: str
query: str
cookie: str
value_prefix: str
def __init__(
self,
header: _Optional[str] = ...,
query: _Optional[str] = ...,
cookie: _Optional[str] = ...,
value_prefix: _Optional[str] = ...,
) -> None: ...
class AuthProvider(_message.Message):
__slots__ = (
"id",
"issuer",
"jwks_uri",
"audiences",
"authorization_url",
"jwt_locations",
)
ID_FIELD_NUMBER: _ClassVar[int]
ISSUER_FIELD_NUMBER: _ClassVar[int]
JWKS_URI_FIELD_NUMBER: _ClassVar[int]
AUDIENCES_FIELD_NUMBER: _ClassVar[int]
AUTHORIZATION_URL_FIELD_NUMBER: _ClassVar[int]
JWT_LOCATIONS_FIELD_NUMBER: _ClassVar[int]
id: str
issuer: str
jwks_uri: str
audiences: str
authorization_url: str
jwt_locations: _containers.RepeatedCompositeFieldContainer[JwtLocation]
def __init__(
self,
id: _Optional[str] = ...,
issuer: _Optional[str] = ...,
jwks_uri: _Optional[str] = ...,
audiences: _Optional[str] = ...,
authorization_url: _Optional[str] = ...,
jwt_locations: _Optional[_Iterable[_Union[JwtLocation, _Mapping]]] = ...,
) -> None: ...
class OAuthRequirements(_message.Message):
__slots__ = ("canonical_scopes",)
CANONICAL_SCOPES_FIELD_NUMBER: _ClassVar[int]
canonical_scopes: str
def __init__(self, canonical_scopes: _Optional[str] = ...) -> None: ...
class AuthRequirement(_message.Message):
__slots__ = ("provider_id", "audiences")
PROVIDER_ID_FIELD_NUMBER: _ClassVar[int]
AUDIENCES_FIELD_NUMBER: _ClassVar[int]
provider_id: str
audiences: str
def __init__(
self, provider_id: _Optional[str] = ..., audiences: _Optional[str] = ...
) -> None: ...

View File

@@ -0,0 +1,185 @@
// Copyright 2025 Google LLC
//
// 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.
syntax = "proto3";
package google.api;
option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig";
option java_multiple_files = true;
option java_outer_classname = "BackendProto";
option java_package = "com.google.api";
option objc_class_prefix = "GAPI";
// `Backend` defines the backend configuration for a service.
message Backend {
// A list of API backend rules that apply to individual API methods.
//
// **NOTE:** All service configuration rules follow "last one wins" order.
repeated BackendRule rules = 1;
}
// A backend rule provides configuration for an individual API element.
message BackendRule {
// Path Translation specifies how to combine the backend address with the
// request path in order to produce the appropriate forwarding URL for the
// request.
//
// Path Translation is applicable only to HTTP-based backends. Backends which
// do not accept requests over HTTP/HTTPS should leave `path_translation`
// unspecified.
enum PathTranslation {
PATH_TRANSLATION_UNSPECIFIED = 0;
// Use the backend address as-is, with no modification to the path. If the
// URL pattern contains variables, the variable names and values will be
// appended to the query string. If a query string parameter and a URL
// pattern variable have the same name, this may result in duplicate keys in
// the query string.
//
// # Examples
//
// Given the following operation config:
//
// Method path: /api/company/{cid}/user/{uid}
// Backend address: https://example.cloudfunctions.net/getUser
//
// Requests to the following request paths will call the backend at the
// translated path:
//
// Request path: /api/company/widgetworks/user/johndoe
// Translated:
// https://example.cloudfunctions.net/getUser?cid=widgetworks&uid=johndoe
//
// Request path: /api/company/widgetworks/user/johndoe?timezone=EST
// Translated:
// https://example.cloudfunctions.net/getUser?timezone=EST&cid=widgetworks&uid=johndoe
CONSTANT_ADDRESS = 1;
// The request path will be appended to the backend address.
//
// # Examples
//
// Given the following operation config:
//
// Method path: /api/company/{cid}/user/{uid}
// Backend address: https://example.appspot.com
//
// Requests to the following request paths will call the backend at the
// translated path:
//
// Request path: /api/company/widgetworks/user/johndoe
// Translated:
// https://example.appspot.com/api/company/widgetworks/user/johndoe
//
// Request path: /api/company/widgetworks/user/johndoe?timezone=EST
// Translated:
// https://example.appspot.com/api/company/widgetworks/user/johndoe?timezone=EST
APPEND_PATH_TO_ADDRESS = 2;
}
// Selects the methods to which this rule applies.
//
// Refer to [selector][google.api.DocumentationRule.selector] for syntax
// details.
string selector = 1;
// The address of the API backend.
//
// The scheme is used to determine the backend protocol and security.
// The following schemes are accepted:
//
// SCHEME PROTOCOL SECURITY
// http:// HTTP None
// https:// HTTP TLS
// grpc:// gRPC None
// grpcs:// gRPC TLS
//
// It is recommended to explicitly include a scheme. Leaving out the scheme
// may cause constrasting behaviors across platforms.
//
// If the port is unspecified, the default is:
// - 80 for schemes without TLS
// - 443 for schemes with TLS
//
// For HTTP backends, use [protocol][google.api.BackendRule.protocol]
// to specify the protocol version.
string address = 2;
// The number of seconds to wait for a response from a request. The default
// varies based on the request protocol and deployment environment.
double deadline = 3;
// Deprecated, do not use.
double min_deadline = 4 [deprecated = true];
// The number of seconds to wait for the completion of a long running
// operation. The default is no deadline.
double operation_deadline = 5;
PathTranslation path_translation = 6;
// Authentication settings used by the backend.
//
// These are typically used to provide service management functionality to
// a backend served on a publicly-routable URL. The `authentication`
// details should match the authentication behavior used by the backend.
//
// For example, specifying `jwt_audience` implies that the backend expects
// authentication via a JWT.
//
// When authentication is unspecified, the resulting behavior is the same
// as `disable_auth` set to `true`.
//
// Refer to https://developers.google.com/identity/protocols/OpenIDConnect for
// JWT ID token.
oneof authentication {
// The JWT audience is used when generating a JWT ID token for the backend.
// This ID token will be added in the HTTP "authorization" header, and sent
// to the backend.
string jwt_audience = 7;
// When disable_auth is true, a JWT ID token won't be generated and the
// original "Authorization" HTTP header will be preserved. If the header is
// used to carry the original token and is expected by the backend, this
// field must be set to true to preserve the header.
bool disable_auth = 8;
}
// The protocol used for sending a request to the backend.
// The supported values are "http/1.1" and "h2".
//
// The default value is inferred from the scheme in the
// [address][google.api.BackendRule.address] field:
//
// SCHEME PROTOCOL
// http:// http/1.1
// https:// http/1.1
// grpc:// h2
// grpcs:// h2
//
// For secure HTTP backends (https://) that support HTTP/2, set this field
// to "h2" for improved performance.
//
// Configuring this field to non-default values is only supported for secure
// HTTP backends. This field will be ignored for all other backends.
//
// See
// https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
// for more details on the supported values.
string protocol = 9;
// The map between request protocol and the backend address.
map<string, BackendRule> overrides_by_request_protocol = 10;
}

View File

@@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
# Copyright 2025 Google LLC
#
# 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.
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: google/api/backend.proto
# Protobuf Python Version: 4.25.3
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
b'\n\x18google/api/backend.proto\x12\ngoogle.api"1\n\x07\x42\x61\x63kend\x12&\n\x05rules\x18\x01 \x03(\x0b\x32\x17.google.api.BackendRule"\xb2\x04\n\x0b\x42\x61\x63kendRule\x12\x10\n\x08selector\x18\x01 \x01(\t\x12\x0f\n\x07\x61\x64\x64ress\x18\x02 \x01(\t\x12\x10\n\x08\x64\x65\x61\x64line\x18\x03 \x01(\x01\x12\x18\n\x0cmin_deadline\x18\x04 \x01(\x01\x42\x02\x18\x01\x12\x1a\n\x12operation_deadline\x18\x05 \x01(\x01\x12\x41\n\x10path_translation\x18\x06 \x01(\x0e\x32\'.google.api.BackendRule.PathTranslation\x12\x16\n\x0cjwt_audience\x18\x07 \x01(\tH\x00\x12\x16\n\x0c\x64isable_auth\x18\x08 \x01(\x08H\x00\x12\x10\n\x08protocol\x18\t \x01(\t\x12^\n\x1doverrides_by_request_protocol\x18\n \x03(\x0b\x32\x37.google.api.BackendRule.OverridesByRequestProtocolEntry\x1aZ\n\x1fOverridesByRequestProtocolEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12&\n\x05value\x18\x02 \x01(\x0b\x32\x17.google.api.BackendRule:\x02\x38\x01"e\n\x0fPathTranslation\x12 \n\x1cPATH_TRANSLATION_UNSPECIFIED\x10\x00\x12\x14\n\x10\x43ONSTANT_ADDRESS\x10\x01\x12\x1a\n\x16\x41PPEND_PATH_TO_ADDRESS\x10\x02\x42\x10\n\x0e\x61uthenticationBn\n\x0e\x63om.google.apiB\x0c\x42\x61\x63kendProtoP\x01ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\xa2\x02\x04GAPIb\x06proto3'
)
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.api.backend_pb2", _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
_globals["DESCRIPTOR"]._options = None
_globals[
"DESCRIPTOR"
]._serialized_options = b"\n\016com.google.apiB\014BackendProtoP\001ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\242\002\004GAPI"
_globals["_BACKENDRULE_OVERRIDESBYREQUESTPROTOCOLENTRY"]._options = None
_globals[
"_BACKENDRULE_OVERRIDESBYREQUESTPROTOCOLENTRY"
]._serialized_options = b"8\001"
_globals["_BACKENDRULE"].fields_by_name["min_deadline"]._options = None
_globals["_BACKENDRULE"].fields_by_name[
"min_deadline"
]._serialized_options = b"\030\001"
_globals["_BACKEND"]._serialized_start = 40
_globals["_BACKEND"]._serialized_end = 89
_globals["_BACKENDRULE"]._serialized_start = 92
_globals["_BACKENDRULE"]._serialized_end = 654
_globals["_BACKENDRULE_OVERRIDESBYREQUESTPROTOCOLENTRY"]._serialized_start = 443
_globals["_BACKENDRULE_OVERRIDESBYREQUESTPROTOCOLENTRY"]._serialized_end = 533
_globals["_BACKENDRULE_PATHTRANSLATION"]._serialized_start = 535
_globals["_BACKENDRULE_PATHTRANSLATION"]._serialized_end = 636
# @@protoc_insertion_point(module_scope)

View File

@@ -0,0 +1,102 @@
# Copyright 2025 Google LLC
#
# 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 typing import ClassVar as _ClassVar
from typing import Iterable as _Iterable
from typing import Mapping as _Mapping
from typing import Optional as _Optional
from typing import Union as _Union
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf.internal import containers as _containers
from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper
DESCRIPTOR: _descriptor.FileDescriptor
class Backend(_message.Message):
__slots__ = ("rules",)
RULES_FIELD_NUMBER: _ClassVar[int]
rules: _containers.RepeatedCompositeFieldContainer[BackendRule]
def __init__(
self, rules: _Optional[_Iterable[_Union[BackendRule, _Mapping]]] = ...
) -> None: ...
class BackendRule(_message.Message):
__slots__ = (
"selector",
"address",
"deadline",
"min_deadline",
"operation_deadline",
"path_translation",
"jwt_audience",
"disable_auth",
"protocol",
"overrides_by_request_protocol",
)
class PathTranslation(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
__slots__ = ()
PATH_TRANSLATION_UNSPECIFIED: _ClassVar[BackendRule.PathTranslation]
CONSTANT_ADDRESS: _ClassVar[BackendRule.PathTranslation]
APPEND_PATH_TO_ADDRESS: _ClassVar[BackendRule.PathTranslation]
PATH_TRANSLATION_UNSPECIFIED: BackendRule.PathTranslation
CONSTANT_ADDRESS: BackendRule.PathTranslation
APPEND_PATH_TO_ADDRESS: BackendRule.PathTranslation
class OverridesByRequestProtocolEntry(_message.Message):
__slots__ = ("key", "value")
KEY_FIELD_NUMBER: _ClassVar[int]
VALUE_FIELD_NUMBER: _ClassVar[int]
key: str
value: BackendRule
def __init__(
self,
key: _Optional[str] = ...,
value: _Optional[_Union[BackendRule, _Mapping]] = ...,
) -> None: ...
SELECTOR_FIELD_NUMBER: _ClassVar[int]
ADDRESS_FIELD_NUMBER: _ClassVar[int]
DEADLINE_FIELD_NUMBER: _ClassVar[int]
MIN_DEADLINE_FIELD_NUMBER: _ClassVar[int]
OPERATION_DEADLINE_FIELD_NUMBER: _ClassVar[int]
PATH_TRANSLATION_FIELD_NUMBER: _ClassVar[int]
JWT_AUDIENCE_FIELD_NUMBER: _ClassVar[int]
DISABLE_AUTH_FIELD_NUMBER: _ClassVar[int]
PROTOCOL_FIELD_NUMBER: _ClassVar[int]
OVERRIDES_BY_REQUEST_PROTOCOL_FIELD_NUMBER: _ClassVar[int]
selector: str
address: str
deadline: float
min_deadline: float
operation_deadline: float
path_translation: BackendRule.PathTranslation
jwt_audience: str
disable_auth: bool
protocol: str
overrides_by_request_protocol: _containers.MessageMap[str, BackendRule]
def __init__(
self,
selector: _Optional[str] = ...,
address: _Optional[str] = ...,
deadline: _Optional[float] = ...,
min_deadline: _Optional[float] = ...,
operation_deadline: _Optional[float] = ...,
path_translation: _Optional[_Union[BackendRule.PathTranslation, str]] = ...,
jwt_audience: _Optional[str] = ...,
disable_auth: bool = ...,
protocol: _Optional[str] = ...,
overrides_by_request_protocol: _Optional[_Mapping[str, BackendRule]] = ...,
) -> None: ...

View File

@@ -0,0 +1,77 @@
// Copyright 2025 Google LLC
//
// 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.
syntax = "proto3";
package google.api;
option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig";
option java_multiple_files = true;
option java_outer_classname = "BillingProto";
option java_package = "com.google.api";
option objc_class_prefix = "GAPI";
// Billing related configuration of the service.
//
// The following example shows how to configure monitored resources and metrics
// for billing, `consumer_destinations` is the only supported destination and
// the monitored resources need at least one label key
// `cloud.googleapis.com/location` to indicate the location of the billing
// usage, using different monitored resources between monitoring and billing is
// recommended so they can be evolved independently:
//
//
// monitored_resources:
// - type: library.googleapis.com/billing_branch
// labels:
// - key: cloud.googleapis.com/location
// description: |
// Predefined label to support billing location restriction.
// - key: city
// description: |
// Custom label to define the city where the library branch is located
// in.
// - key: name
// description: Custom label to define the name of the library branch.
// metrics:
// - name: library.googleapis.com/book/borrowed_count
// metric_kind: DELTA
// value_type: INT64
// unit: "1"
// billing:
// consumer_destinations:
// - monitored_resource: library.googleapis.com/billing_branch
// metrics:
// - library.googleapis.com/book/borrowed_count
message Billing {
// Configuration of a specific billing destination (Currently only support
// bill against consumer project).
message BillingDestination {
// The monitored resource type. The type must be defined in
// [Service.monitored_resources][google.api.Service.monitored_resources]
// section.
string monitored_resource = 1;
// Names of the metrics to report to this billing destination.
// Each name must be defined in
// [Service.metrics][google.api.Service.metrics] section.
repeated string metrics = 2;
}
// Billing configurations for sending metrics to the consumer project.
// There can be multiple consumer destinations per service, each one must have
// a different monitored resource type. A metric can be used in at most
// one consumer destination.
repeated BillingDestination consumer_destinations = 8;
}

View File

@@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
# Copyright 2025 Google LLC
#
# 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.
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: google/api/billing.proto
# Protobuf Python Version: 4.25.3
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
b'\n\x18google/api/billing.proto\x12\ngoogle.api"\x93\x01\n\x07\x42illing\x12\x45\n\x15\x63onsumer_destinations\x18\x08 \x03(\x0b\x32&.google.api.Billing.BillingDestination\x1a\x41\n\x12\x42illingDestination\x12\x1a\n\x12monitored_resource\x18\x01 \x01(\t\x12\x0f\n\x07metrics\x18\x02 \x03(\tBn\n\x0e\x63om.google.apiB\x0c\x42illingProtoP\x01ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\xa2\x02\x04GAPIb\x06proto3'
)
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.api.billing_pb2", _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
_globals["DESCRIPTOR"]._options = None
_globals[
"DESCRIPTOR"
]._serialized_options = b"\n\016com.google.apiB\014BillingProtoP\001ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\242\002\004GAPI"
_globals["_BILLING"]._serialized_start = 41
_globals["_BILLING"]._serialized_end = 188
_globals["_BILLING_BILLINGDESTINATION"]._serialized_start = 123
_globals["_BILLING_BILLINGDESTINATION"]._serialized_end = 188
# @@protoc_insertion_point(module_scope)

View File

@@ -0,0 +1,50 @@
# Copyright 2025 Google LLC
#
# 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 typing import ClassVar as _ClassVar
from typing import Iterable as _Iterable
from typing import Mapping as _Mapping
from typing import Optional as _Optional
from typing import Union as _Union
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf.internal import containers as _containers
DESCRIPTOR: _descriptor.FileDescriptor
class Billing(_message.Message):
__slots__ = ("consumer_destinations",)
class BillingDestination(_message.Message):
__slots__ = ("monitored_resource", "metrics")
MONITORED_RESOURCE_FIELD_NUMBER: _ClassVar[int]
METRICS_FIELD_NUMBER: _ClassVar[int]
monitored_resource: str
metrics: _containers.RepeatedScalarFieldContainer[str]
def __init__(
self,
monitored_resource: _Optional[str] = ...,
metrics: _Optional[_Iterable[str]] = ...,
) -> None: ...
CONSUMER_DESTINATIONS_FIELD_NUMBER: _ClassVar[int]
consumer_destinations: _containers.RepeatedCompositeFieldContainer[
Billing.BillingDestination
]
def __init__(
self,
consumer_destinations: _Optional[
_Iterable[_Union[Billing.BillingDestination, _Mapping]]
] = ...,
) -> None: ...

View File

@@ -0,0 +1,486 @@
// Copyright 2025 Google LLC
//
// 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.
syntax = "proto3";
package google.api;
import "google/api/launch_stage.proto";
import "google/protobuf/descriptor.proto";
import "google/protobuf/duration.proto";
option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
option java_multiple_files = true;
option java_outer_classname = "ClientProto";
option java_package = "com.google.api";
option objc_class_prefix = "GAPI";
extend google.protobuf.MethodOptions {
// A definition of a client library method signature.
//
// In client libraries, each proto RPC corresponds to one or more methods
// which the end user is able to call, and calls the underlying RPC.
// Normally, this method receives a single argument (a struct or instance
// corresponding to the RPC request object). Defining this field will
// add one or more overloads providing flattened or simpler method signatures
// in some languages.
//
// The fields on the method signature are provided as a comma-separated
// string.
//
// For example, the proto RPC and annotation:
//
// rpc CreateSubscription(CreateSubscriptionRequest)
// returns (Subscription) {
// option (google.api.method_signature) = "name,topic";
// }
//
// Would add the following Java overload (in addition to the method accepting
// the request object):
//
// public final Subscription createSubscription(String name, String topic)
//
// The following backwards-compatibility guidelines apply:
//
// * Adding this annotation to an unannotated method is backwards
// compatible.
// * Adding this annotation to a method which already has existing
// method signature annotations is backwards compatible if and only if
// the new method signature annotation is last in the sequence.
// * Modifying or removing an existing method signature annotation is
// a breaking change.
// * Re-ordering existing method signature annotations is a breaking
// change.
repeated string method_signature = 1051;
}
extend google.protobuf.ServiceOptions {
// The hostname for this service.
// This should be specified with no prefix or protocol.
//
// Example:
//
// service Foo {
// option (google.api.default_host) = "foo.googleapi.com";
// ...
// }
string default_host = 1049;
// OAuth scopes needed for the client.
//
// Example:
//
// service Foo {
// option (google.api.oauth_scopes) = \
// "https://www.googleapis.com/auth/cloud-platform";
// ...
// }
//
// If there is more than one scope, use a comma-separated string:
//
// Example:
//
// service Foo {
// option (google.api.oauth_scopes) = \
// "https://www.googleapis.com/auth/cloud-platform,"
// "https://www.googleapis.com/auth/monitoring";
// ...
// }
string oauth_scopes = 1050;
// The API version of this service, which should be sent by version-aware
// clients to the service. This allows services to abide by the schema and
// behavior of the service at the time this API version was deployed.
// The format of the API version must be treated as opaque by clients.
// Services may use a format with an apparent structure, but clients must
// not rely on this to determine components within an API version, or attempt
// to construct other valid API versions. Note that this is for upcoming
// functionality and may not be implemented for all services.
//
// Example:
//
// service Foo {
// option (google.api.api_version) = "v1_20230821_preview";
// }
string api_version = 525000001;
}
// Required information for every language.
message CommonLanguageSettings {
// Link to automatically generated reference documentation. Example:
// https://cloud.google.com/nodejs/docs/reference/asset/latest
string reference_docs_uri = 1 [deprecated = true];
// The destination where API teams want this client library to be published.
repeated ClientLibraryDestination destinations = 2;
// Configuration for which RPCs should be generated in the GAPIC client.
SelectiveGapicGeneration selective_gapic_generation = 3;
}
// Details about how and where to publish client libraries.
message ClientLibrarySettings {
// Version of the API to apply these settings to. This is the full protobuf
// package for the API, ending in the version element.
// Examples: "google.cloud.speech.v1" and "google.spanner.admin.database.v1".
string version = 1;
// Launch stage of this version of the API.
LaunchStage launch_stage = 2;
// When using transport=rest, the client request will encode enums as
// numbers rather than strings.
bool rest_numeric_enums = 3;
// Settings for legacy Java features, supported in the Service YAML.
JavaSettings java_settings = 21;
// Settings for C++ client libraries.
CppSettings cpp_settings = 22;
// Settings for PHP client libraries.
PhpSettings php_settings = 23;
// Settings for Python client libraries.
PythonSettings python_settings = 24;
// Settings for Node client libraries.
NodeSettings node_settings = 25;
// Settings for .NET client libraries.
DotnetSettings dotnet_settings = 26;
// Settings for Ruby client libraries.
RubySettings ruby_settings = 27;
// Settings for Go client libraries.
GoSettings go_settings = 28;
}
// This message configures the settings for publishing [Google Cloud Client
// libraries](https://cloud.google.com/apis/docs/cloud-client-libraries)
// generated from the service config.
message Publishing {
// A list of API method settings, e.g. the behavior for methods that use the
// long-running operation pattern.
repeated MethodSettings method_settings = 2;
// Link to a *public* URI where users can report issues. Example:
// https://issuetracker.google.com/issues/new?component=190865&template=1161103
string new_issue_uri = 101;
// Link to product home page. Example:
// https://cloud.google.com/asset-inventory/docs/overview
string documentation_uri = 102;
// Used as a tracking tag when collecting data about the APIs developer
// relations artifacts like docs, packages delivered to package managers,
// etc. Example: "speech".
string api_short_name = 103;
// GitHub label to apply to issues and pull requests opened for this API.
string github_label = 104;
// GitHub teams to be added to CODEOWNERS in the directory in GitHub
// containing source code for the client libraries for this API.
repeated string codeowner_github_teams = 105;
// A prefix used in sample code when demarking regions to be included in
// documentation.
string doc_tag_prefix = 106;
// For whom the client library is being published.
ClientLibraryOrganization organization = 107;
// Client library settings. If the same version string appears multiple
// times in this list, then the last one wins. Settings from earlier
// settings with the same version string are discarded.
repeated ClientLibrarySettings library_settings = 109;
// Optional link to proto reference documentation. Example:
// https://cloud.google.com/pubsub/lite/docs/reference/rpc
string proto_reference_documentation_uri = 110;
// Optional link to REST reference documentation. Example:
// https://cloud.google.com/pubsub/lite/docs/reference/rest
string rest_reference_documentation_uri = 111;
}
// Settings for Java client libraries.
message JavaSettings {
// The package name to use in Java. Clobbers the java_package option
// set in the protobuf. This should be used **only** by APIs
// who have already set the language_settings.java.package_name" field
// in gapic.yaml. API teams should use the protobuf java_package option
// where possible.
//
// Example of a YAML configuration::
//
// publishing:
// java_settings:
// library_package: com.google.cloud.pubsub.v1
string library_package = 1;
// Configure the Java class name to use instead of the service's for its
// corresponding generated GAPIC client. Keys are fully-qualified
// service names as they appear in the protobuf (including the full
// the language_settings.java.interface_names" field in gapic.yaml. API
// teams should otherwise use the service name as it appears in the
// protobuf.
//
// Example of a YAML configuration::
//
// publishing:
// java_settings:
// service_class_names:
// - google.pubsub.v1.Publisher: TopicAdmin
// - google.pubsub.v1.Subscriber: SubscriptionAdmin
map<string, string> service_class_names = 2;
// Some settings.
CommonLanguageSettings common = 3;
}
// Settings for C++ client libraries.
message CppSettings {
// Some settings.
CommonLanguageSettings common = 1;
}
// Settings for Php client libraries.
message PhpSettings {
// Some settings.
CommonLanguageSettings common = 1;
}
// Settings for Python client libraries.
message PythonSettings {
// Experimental features to be included during client library generation.
// These fields will be deprecated once the feature graduates and is enabled
// by default.
message ExperimentalFeatures {
// Enables generation of asynchronous REST clients if `rest` transport is
// enabled. By default, asynchronous REST clients will not be generated.
// This feature will be enabled by default 1 month after launching the
// feature in preview packages.
bool rest_async_io_enabled = 1;
// Enables generation of protobuf code using new types that are more
// Pythonic which are included in `protobuf>=5.29.x`. This feature will be
// enabled by default 1 month after launching the feature in preview
// packages.
bool protobuf_pythonic_types_enabled = 2;
// Disables generation of an unversioned Python package for this client
// library. This means that the module names will need to be versioned in
// import statements. For example `import google.cloud.library_v2` instead
// of `import google.cloud.library`.
bool unversioned_package_disabled = 3;
}
// Some settings.
CommonLanguageSettings common = 1;
// Experimental features to be included during client library generation.
ExperimentalFeatures experimental_features = 2;
}
// Settings for Node client libraries.
message NodeSettings {
// Some settings.
CommonLanguageSettings common = 1;
}
// Settings for Dotnet client libraries.
message DotnetSettings {
// Some settings.
CommonLanguageSettings common = 1;
// Map from original service names to renamed versions.
// This is used when the default generated types
// would cause a naming conflict. (Neither name is
// fully-qualified.)
// Example: Subscriber to SubscriberServiceApi.
map<string, string> renamed_services = 2;
// Map from full resource types to the effective short name
// for the resource. This is used when otherwise resource
// named from different services would cause naming collisions.
// Example entry:
// "datalabeling.googleapis.com/Dataset": "DataLabelingDataset"
map<string, string> renamed_resources = 3;
// List of full resource types to ignore during generation.
// This is typically used for API-specific Location resources,
// which should be handled by the generator as if they were actually
// the common Location resources.
// Example entry: "documentai.googleapis.com/Location"
repeated string ignored_resources = 4;
// Namespaces which must be aliased in snippets due to
// a known (but non-generator-predictable) naming collision
repeated string forced_namespace_aliases = 5;
// Method signatures (in the form "service.method(signature)")
// which are provided separately, so shouldn't be generated.
// Snippets *calling* these methods are still generated, however.
repeated string handwritten_signatures = 6;
}
// Settings for Ruby client libraries.
message RubySettings {
// Some settings.
CommonLanguageSettings common = 1;
}
// Settings for Go client libraries.
message GoSettings {
// Some settings.
CommonLanguageSettings common = 1;
// Map of service names to renamed services. Keys are the package relative
// service names and values are the name to be used for the service client
// and call options.
//
// publishing:
// go_settings:
// renamed_services:
// Publisher: TopicAdmin
map<string, string> renamed_services = 2;
}
// Describes the generator configuration for a method.
message MethodSettings {
// Describes settings to use when generating API methods that use the
// long-running operation pattern.
// All default values below are from those used in the client library
// generators (e.g.
// [Java](https://github.com/googleapis/gapic-generator-java/blob/04c2faa191a9b5a10b92392fe8482279c4404803/src/main/java/com/google/api/generator/gapic/composer/common/RetrySettingsComposer.java)).
message LongRunning {
// Initial delay after which the first poll request will be made.
// Default value: 5 seconds.
google.protobuf.Duration initial_poll_delay = 1;
// Multiplier to gradually increase delay between subsequent polls until it
// reaches max_poll_delay.
// Default value: 1.5.
float poll_delay_multiplier = 2;
// Maximum time between two subsequent poll requests.
// Default value: 45 seconds.
google.protobuf.Duration max_poll_delay = 3;
// Total polling timeout.
// Default value: 5 minutes.
google.protobuf.Duration total_poll_timeout = 4;
}
// The fully qualified name of the method, for which the options below apply.
// This is used to find the method to apply the options.
//
// Example:
//
// publishing:
// method_settings:
// - selector: google.storage.control.v2.StorageControl.CreateFolder
// # method settings for CreateFolder...
string selector = 1;
// Describes settings to use for long-running operations when generating
// API methods for RPCs. Complements RPCs that use the annotations in
// google/longrunning/operations.proto.
//
// Example of a YAML configuration::
//
// publishing:
// method_settings:
// - selector: google.cloud.speech.v2.Speech.BatchRecognize
// long_running:
// initial_poll_delay: 60s # 1 minute
// poll_delay_multiplier: 1.5
// max_poll_delay: 360s # 6 minutes
// total_poll_timeout: 54000s # 90 minutes
LongRunning long_running = 2;
// List of top-level fields of the request message, that should be
// automatically populated by the client libraries based on their
// (google.api.field_info).format. Currently supported format: UUID4.
//
// Example of a YAML configuration:
//
// publishing:
// method_settings:
// - selector: google.example.v1.ExampleService.CreateExample
// auto_populated_fields:
// - request_id
repeated string auto_populated_fields = 3;
}
// The organization for which the client libraries are being published.
// Affects the url where generated docs are published, etc.
enum ClientLibraryOrganization {
// Not useful.
CLIENT_LIBRARY_ORGANIZATION_UNSPECIFIED = 0;
// Google Cloud Platform Org.
CLOUD = 1;
// Ads (Advertising) Org.
ADS = 2;
// Photos Org.
PHOTOS = 3;
// Street View Org.
STREET_VIEW = 4;
// Shopping Org.
SHOPPING = 5;
// Geo Org.
GEO = 6;
// Generative AI - https://developers.generativeai.google
GENERATIVE_AI = 7;
}
// To where should client libraries be published?
enum ClientLibraryDestination {
// Client libraries will neither be generated nor published to package
// managers.
CLIENT_LIBRARY_DESTINATION_UNSPECIFIED = 0;
// Generate the client library in a repo under github.com/googleapis,
// but don't publish it to package managers.
GITHUB = 10;
// Publish the library to package managers like nuget.org and npmjs.com.
PACKAGE_MANAGER = 20;
}
// This message is used to configure the generation of a subset of the RPCs in
// a service for client libraries.
message SelectiveGapicGeneration {
// An allowlist of the fully qualified names of RPCs that should be included
// on public client surfaces.
repeated string methods = 1;
// Setting this to true indicates to the client generators that methods
// that would be excluded from the generation should instead be generated
// in a way that indicates these methods should not be consumed by
// end users. How this is expressed is up to individual language
// implementations to decide. Some examples may be: added annotations,
// obfuscated identifiers, or other language idiomatic patterns.
bool generate_omitted_as_internal = 2;
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,404 @@
# Copyright 2025 Google LLC
#
# 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 typing import ClassVar as _ClassVar
from typing import Iterable as _Iterable
from typing import Mapping as _Mapping
from typing import Optional as _Optional
from typing import Union as _Union
from google.api import launch_stage_pb2 as _launch_stage_pb2
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pb2 as _descriptor_pb2
from google.protobuf import duration_pb2 as _duration_pb2
from google.protobuf import message as _message
from google.protobuf.internal import containers as _containers
from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper
DESCRIPTOR: _descriptor.FileDescriptor
class ClientLibraryOrganization(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
__slots__ = ()
CLIENT_LIBRARY_ORGANIZATION_UNSPECIFIED: _ClassVar[ClientLibraryOrganization]
CLOUD: _ClassVar[ClientLibraryOrganization]
ADS: _ClassVar[ClientLibraryOrganization]
PHOTOS: _ClassVar[ClientLibraryOrganization]
STREET_VIEW: _ClassVar[ClientLibraryOrganization]
SHOPPING: _ClassVar[ClientLibraryOrganization]
GEO: _ClassVar[ClientLibraryOrganization]
GENERATIVE_AI: _ClassVar[ClientLibraryOrganization]
class ClientLibraryDestination(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
__slots__ = ()
CLIENT_LIBRARY_DESTINATION_UNSPECIFIED: _ClassVar[ClientLibraryDestination]
GITHUB: _ClassVar[ClientLibraryDestination]
PACKAGE_MANAGER: _ClassVar[ClientLibraryDestination]
CLIENT_LIBRARY_ORGANIZATION_UNSPECIFIED: ClientLibraryOrganization
CLOUD: ClientLibraryOrganization
ADS: ClientLibraryOrganization
PHOTOS: ClientLibraryOrganization
STREET_VIEW: ClientLibraryOrganization
SHOPPING: ClientLibraryOrganization
GEO: ClientLibraryOrganization
GENERATIVE_AI: ClientLibraryOrganization
CLIENT_LIBRARY_DESTINATION_UNSPECIFIED: ClientLibraryDestination
GITHUB: ClientLibraryDestination
PACKAGE_MANAGER: ClientLibraryDestination
METHOD_SIGNATURE_FIELD_NUMBER: _ClassVar[int]
method_signature: _descriptor.FieldDescriptor
DEFAULT_HOST_FIELD_NUMBER: _ClassVar[int]
default_host: _descriptor.FieldDescriptor
OAUTH_SCOPES_FIELD_NUMBER: _ClassVar[int]
oauth_scopes: _descriptor.FieldDescriptor
API_VERSION_FIELD_NUMBER: _ClassVar[int]
api_version: _descriptor.FieldDescriptor
class CommonLanguageSettings(_message.Message):
__slots__ = ("reference_docs_uri", "destinations", "selective_gapic_generation")
REFERENCE_DOCS_URI_FIELD_NUMBER: _ClassVar[int]
DESTINATIONS_FIELD_NUMBER: _ClassVar[int]
SELECTIVE_GAPIC_GENERATION_FIELD_NUMBER: _ClassVar[int]
reference_docs_uri: str
destinations: _containers.RepeatedScalarFieldContainer[ClientLibraryDestination]
selective_gapic_generation: SelectiveGapicGeneration
def __init__(
self,
reference_docs_uri: _Optional[str] = ...,
destinations: _Optional[_Iterable[_Union[ClientLibraryDestination, str]]] = ...,
selective_gapic_generation: _Optional[
_Union[SelectiveGapicGeneration, _Mapping]
] = ...,
) -> None: ...
class ClientLibrarySettings(_message.Message):
__slots__ = (
"version",
"launch_stage",
"rest_numeric_enums",
"java_settings",
"cpp_settings",
"php_settings",
"python_settings",
"node_settings",
"dotnet_settings",
"ruby_settings",
"go_settings",
)
VERSION_FIELD_NUMBER: _ClassVar[int]
LAUNCH_STAGE_FIELD_NUMBER: _ClassVar[int]
REST_NUMERIC_ENUMS_FIELD_NUMBER: _ClassVar[int]
JAVA_SETTINGS_FIELD_NUMBER: _ClassVar[int]
CPP_SETTINGS_FIELD_NUMBER: _ClassVar[int]
PHP_SETTINGS_FIELD_NUMBER: _ClassVar[int]
PYTHON_SETTINGS_FIELD_NUMBER: _ClassVar[int]
NODE_SETTINGS_FIELD_NUMBER: _ClassVar[int]
DOTNET_SETTINGS_FIELD_NUMBER: _ClassVar[int]
RUBY_SETTINGS_FIELD_NUMBER: _ClassVar[int]
GO_SETTINGS_FIELD_NUMBER: _ClassVar[int]
version: str
launch_stage: _launch_stage_pb2.LaunchStage
rest_numeric_enums: bool
java_settings: JavaSettings
cpp_settings: CppSettings
php_settings: PhpSettings
python_settings: PythonSettings
node_settings: NodeSettings
dotnet_settings: DotnetSettings
ruby_settings: RubySettings
go_settings: GoSettings
def __init__(
self,
version: _Optional[str] = ...,
launch_stage: _Optional[_Union[_launch_stage_pb2.LaunchStage, str]] = ...,
rest_numeric_enums: bool = ...,
java_settings: _Optional[_Union[JavaSettings, _Mapping]] = ...,
cpp_settings: _Optional[_Union[CppSettings, _Mapping]] = ...,
php_settings: _Optional[_Union[PhpSettings, _Mapping]] = ...,
python_settings: _Optional[_Union[PythonSettings, _Mapping]] = ...,
node_settings: _Optional[_Union[NodeSettings, _Mapping]] = ...,
dotnet_settings: _Optional[_Union[DotnetSettings, _Mapping]] = ...,
ruby_settings: _Optional[_Union[RubySettings, _Mapping]] = ...,
go_settings: _Optional[_Union[GoSettings, _Mapping]] = ...,
) -> None: ...
class Publishing(_message.Message):
__slots__ = (
"method_settings",
"new_issue_uri",
"documentation_uri",
"api_short_name",
"github_label",
"codeowner_github_teams",
"doc_tag_prefix",
"organization",
"library_settings",
"proto_reference_documentation_uri",
"rest_reference_documentation_uri",
)
METHOD_SETTINGS_FIELD_NUMBER: _ClassVar[int]
NEW_ISSUE_URI_FIELD_NUMBER: _ClassVar[int]
DOCUMENTATION_URI_FIELD_NUMBER: _ClassVar[int]
API_SHORT_NAME_FIELD_NUMBER: _ClassVar[int]
GITHUB_LABEL_FIELD_NUMBER: _ClassVar[int]
CODEOWNER_GITHUB_TEAMS_FIELD_NUMBER: _ClassVar[int]
DOC_TAG_PREFIX_FIELD_NUMBER: _ClassVar[int]
ORGANIZATION_FIELD_NUMBER: _ClassVar[int]
LIBRARY_SETTINGS_FIELD_NUMBER: _ClassVar[int]
PROTO_REFERENCE_DOCUMENTATION_URI_FIELD_NUMBER: _ClassVar[int]
REST_REFERENCE_DOCUMENTATION_URI_FIELD_NUMBER: _ClassVar[int]
method_settings: _containers.RepeatedCompositeFieldContainer[MethodSettings]
new_issue_uri: str
documentation_uri: str
api_short_name: str
github_label: str
codeowner_github_teams: _containers.RepeatedScalarFieldContainer[str]
doc_tag_prefix: str
organization: ClientLibraryOrganization
library_settings: _containers.RepeatedCompositeFieldContainer[ClientLibrarySettings]
proto_reference_documentation_uri: str
rest_reference_documentation_uri: str
def __init__(
self,
method_settings: _Optional[_Iterable[_Union[MethodSettings, _Mapping]]] = ...,
new_issue_uri: _Optional[str] = ...,
documentation_uri: _Optional[str] = ...,
api_short_name: _Optional[str] = ...,
github_label: _Optional[str] = ...,
codeowner_github_teams: _Optional[_Iterable[str]] = ...,
doc_tag_prefix: _Optional[str] = ...,
organization: _Optional[_Union[ClientLibraryOrganization, str]] = ...,
library_settings: _Optional[
_Iterable[_Union[ClientLibrarySettings, _Mapping]]
] = ...,
proto_reference_documentation_uri: _Optional[str] = ...,
rest_reference_documentation_uri: _Optional[str] = ...,
) -> None: ...
class JavaSettings(_message.Message):
__slots__ = ("library_package", "service_class_names", "common")
class ServiceClassNamesEntry(_message.Message):
__slots__ = ("key", "value")
KEY_FIELD_NUMBER: _ClassVar[int]
VALUE_FIELD_NUMBER: _ClassVar[int]
key: str
value: str
def __init__(
self, key: _Optional[str] = ..., value: _Optional[str] = ...
) -> None: ...
LIBRARY_PACKAGE_FIELD_NUMBER: _ClassVar[int]
SERVICE_CLASS_NAMES_FIELD_NUMBER: _ClassVar[int]
COMMON_FIELD_NUMBER: _ClassVar[int]
library_package: str
service_class_names: _containers.ScalarMap[str, str]
common: CommonLanguageSettings
def __init__(
self,
library_package: _Optional[str] = ...,
service_class_names: _Optional[_Mapping[str, str]] = ...,
common: _Optional[_Union[CommonLanguageSettings, _Mapping]] = ...,
) -> None: ...
class CppSettings(_message.Message):
__slots__ = ("common",)
COMMON_FIELD_NUMBER: _ClassVar[int]
common: CommonLanguageSettings
def __init__(
self, common: _Optional[_Union[CommonLanguageSettings, _Mapping]] = ...
) -> None: ...
class PhpSettings(_message.Message):
__slots__ = ("common",)
COMMON_FIELD_NUMBER: _ClassVar[int]
common: CommonLanguageSettings
def __init__(
self, common: _Optional[_Union[CommonLanguageSettings, _Mapping]] = ...
) -> None: ...
class PythonSettings(_message.Message):
__slots__ = ("common", "experimental_features")
class ExperimentalFeatures(_message.Message):
__slots__ = (
"rest_async_io_enabled",
"protobuf_pythonic_types_enabled",
"unversioned_package_disabled",
)
REST_ASYNC_IO_ENABLED_FIELD_NUMBER: _ClassVar[int]
PROTOBUF_PYTHONIC_TYPES_ENABLED_FIELD_NUMBER: _ClassVar[int]
UNVERSIONED_PACKAGE_DISABLED_FIELD_NUMBER: _ClassVar[int]
rest_async_io_enabled: bool
protobuf_pythonic_types_enabled: bool
unversioned_package_disabled: bool
def __init__(
self,
rest_async_io_enabled: bool = ...,
protobuf_pythonic_types_enabled: bool = ...,
unversioned_package_disabled: bool = ...,
) -> None: ...
COMMON_FIELD_NUMBER: _ClassVar[int]
EXPERIMENTAL_FEATURES_FIELD_NUMBER: _ClassVar[int]
common: CommonLanguageSettings
experimental_features: PythonSettings.ExperimentalFeatures
def __init__(
self,
common: _Optional[_Union[CommonLanguageSettings, _Mapping]] = ...,
experimental_features: _Optional[
_Union[PythonSettings.ExperimentalFeatures, _Mapping]
] = ...,
) -> None: ...
class NodeSettings(_message.Message):
__slots__ = ("common",)
COMMON_FIELD_NUMBER: _ClassVar[int]
common: CommonLanguageSettings
def __init__(
self, common: _Optional[_Union[CommonLanguageSettings, _Mapping]] = ...
) -> None: ...
class DotnetSettings(_message.Message):
__slots__ = (
"common",
"renamed_services",
"renamed_resources",
"ignored_resources",
"forced_namespace_aliases",
"handwritten_signatures",
)
class RenamedServicesEntry(_message.Message):
__slots__ = ("key", "value")
KEY_FIELD_NUMBER: _ClassVar[int]
VALUE_FIELD_NUMBER: _ClassVar[int]
key: str
value: str
def __init__(
self, key: _Optional[str] = ..., value: _Optional[str] = ...
) -> None: ...
class RenamedResourcesEntry(_message.Message):
__slots__ = ("key", "value")
KEY_FIELD_NUMBER: _ClassVar[int]
VALUE_FIELD_NUMBER: _ClassVar[int]
key: str
value: str
def __init__(
self, key: _Optional[str] = ..., value: _Optional[str] = ...
) -> None: ...
COMMON_FIELD_NUMBER: _ClassVar[int]
RENAMED_SERVICES_FIELD_NUMBER: _ClassVar[int]
RENAMED_RESOURCES_FIELD_NUMBER: _ClassVar[int]
IGNORED_RESOURCES_FIELD_NUMBER: _ClassVar[int]
FORCED_NAMESPACE_ALIASES_FIELD_NUMBER: _ClassVar[int]
HANDWRITTEN_SIGNATURES_FIELD_NUMBER: _ClassVar[int]
common: CommonLanguageSettings
renamed_services: _containers.ScalarMap[str, str]
renamed_resources: _containers.ScalarMap[str, str]
ignored_resources: _containers.RepeatedScalarFieldContainer[str]
forced_namespace_aliases: _containers.RepeatedScalarFieldContainer[str]
handwritten_signatures: _containers.RepeatedScalarFieldContainer[str]
def __init__(
self,
common: _Optional[_Union[CommonLanguageSettings, _Mapping]] = ...,
renamed_services: _Optional[_Mapping[str, str]] = ...,
renamed_resources: _Optional[_Mapping[str, str]] = ...,
ignored_resources: _Optional[_Iterable[str]] = ...,
forced_namespace_aliases: _Optional[_Iterable[str]] = ...,
handwritten_signatures: _Optional[_Iterable[str]] = ...,
) -> None: ...
class RubySettings(_message.Message):
__slots__ = ("common",)
COMMON_FIELD_NUMBER: _ClassVar[int]
common: CommonLanguageSettings
def __init__(
self, common: _Optional[_Union[CommonLanguageSettings, _Mapping]] = ...
) -> None: ...
class GoSettings(_message.Message):
__slots__ = ("common", "renamed_services")
class RenamedServicesEntry(_message.Message):
__slots__ = ("key", "value")
KEY_FIELD_NUMBER: _ClassVar[int]
VALUE_FIELD_NUMBER: _ClassVar[int]
key: str
value: str
def __init__(
self, key: _Optional[str] = ..., value: _Optional[str] = ...
) -> None: ...
COMMON_FIELD_NUMBER: _ClassVar[int]
RENAMED_SERVICES_FIELD_NUMBER: _ClassVar[int]
common: CommonLanguageSettings
renamed_services: _containers.ScalarMap[str, str]
def __init__(
self,
common: _Optional[_Union[CommonLanguageSettings, _Mapping]] = ...,
renamed_services: _Optional[_Mapping[str, str]] = ...,
) -> None: ...
class MethodSettings(_message.Message):
__slots__ = ("selector", "long_running", "auto_populated_fields")
class LongRunning(_message.Message):
__slots__ = (
"initial_poll_delay",
"poll_delay_multiplier",
"max_poll_delay",
"total_poll_timeout",
)
INITIAL_POLL_DELAY_FIELD_NUMBER: _ClassVar[int]
POLL_DELAY_MULTIPLIER_FIELD_NUMBER: _ClassVar[int]
MAX_POLL_DELAY_FIELD_NUMBER: _ClassVar[int]
TOTAL_POLL_TIMEOUT_FIELD_NUMBER: _ClassVar[int]
initial_poll_delay: _duration_pb2.Duration
poll_delay_multiplier: float
max_poll_delay: _duration_pb2.Duration
total_poll_timeout: _duration_pb2.Duration
def __init__(
self,
initial_poll_delay: _Optional[
_Union[_duration_pb2.Duration, _Mapping]
] = ...,
poll_delay_multiplier: _Optional[float] = ...,
max_poll_delay: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ...,
total_poll_timeout: _Optional[
_Union[_duration_pb2.Duration, _Mapping]
] = ...,
) -> None: ...
SELECTOR_FIELD_NUMBER: _ClassVar[int]
LONG_RUNNING_FIELD_NUMBER: _ClassVar[int]
AUTO_POPULATED_FIELDS_FIELD_NUMBER: _ClassVar[int]
selector: str
long_running: MethodSettings.LongRunning
auto_populated_fields: _containers.RepeatedScalarFieldContainer[str]
def __init__(
self,
selector: _Optional[str] = ...,
long_running: _Optional[_Union[MethodSettings.LongRunning, _Mapping]] = ...,
auto_populated_fields: _Optional[_Iterable[str]] = ...,
) -> None: ...
class SelectiveGapicGeneration(_message.Message):
__slots__ = ("methods", "generate_omitted_as_internal")
METHODS_FIELD_NUMBER: _ClassVar[int]
GENERATE_OMITTED_AS_INTERNAL_FIELD_NUMBER: _ClassVar[int]
methods: _containers.RepeatedScalarFieldContainer[str]
generate_omitted_as_internal: bool
def __init__(
self,
methods: _Optional[_Iterable[str]] = ...,
generate_omitted_as_internal: bool = ...,
) -> None: ...

View File

@@ -0,0 +1,84 @@
// Copyright 2025 Google LLC
//
// 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.
syntax = "proto3";
package google.api;
option go_package = "google.golang.org/genproto/googleapis/api/configchange;configchange";
option java_multiple_files = true;
option java_outer_classname = "ConfigChangeProto";
option java_package = "com.google.api";
option objc_class_prefix = "GAPI";
// Output generated from semantically comparing two versions of a service
// configuration.
//
// Includes detailed information about a field that have changed with
// applicable advice about potential consequences for the change, such as
// backwards-incompatibility.
message ConfigChange {
// Object hierarchy path to the change, with levels separated by a '.'
// character. For repeated fields, an applicable unique identifier field is
// used for the index (usually selector, name, or id). For maps, the term
// 'key' is used. If the field has no unique identifier, the numeric index
// is used.
// Examples:
// - visibility.rules[selector=="google.LibraryService.ListBooks"].restriction
// - quota.metric_rules[selector=="google"].metric_costs[key=="reads"].value
// - logging.producer_destinations[0]
string element = 1;
// Value of the changed object in the old Service configuration,
// in JSON format. This field will not be populated if ChangeType == ADDED.
string old_value = 2;
// Value of the changed object in the new Service configuration,
// in JSON format. This field will not be populated if ChangeType == REMOVED.
string new_value = 3;
// The type for this change, either ADDED, REMOVED, or MODIFIED.
ChangeType change_type = 4;
// Collection of advice provided for this change, useful for determining the
// possible impact of this change.
repeated Advice advices = 5;
}
// Generated advice about this change, used for providing more
// information about how a change will affect the existing service.
message Advice {
// Useful description for why this advice was applied and what actions should
// be taken to mitigate any implied risks.
string description = 2;
}
// Classifies set of possible modifications to an object in the service
// configuration.
enum ChangeType {
// No value was provided.
CHANGE_TYPE_UNSPECIFIED = 0;
// The changed object exists in the 'new' service configuration, but not
// in the 'old' service configuration.
ADDED = 1;
// The changed object exists in the 'old' service configuration, but not
// in the 'new' service configuration.
REMOVED = 2;
// The changed object exists in both service configurations, but its value
// is different.
MODIFIED = 3;
}

View File

@@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
# Copyright 2025 Google LLC
#
# 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.
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: google/api/config_change.proto
# Protobuf Python Version: 4.25.3
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
b'\n\x1egoogle/api/config_change.proto\x12\ngoogle.api"\x97\x01\n\x0c\x43onfigChange\x12\x0f\n\x07\x65lement\x18\x01 \x01(\t\x12\x11\n\told_value\x18\x02 \x01(\t\x12\x11\n\tnew_value\x18\x03 \x01(\t\x12+\n\x0b\x63hange_type\x18\x04 \x01(\x0e\x32\x16.google.api.ChangeType\x12#\n\x07\x61\x64vices\x18\x05 \x03(\x0b\x32\x12.google.api.Advice"\x1d\n\x06\x41\x64vice\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t*O\n\nChangeType\x12\x1b\n\x17\x43HANGE_TYPE_UNSPECIFIED\x10\x00\x12\t\n\x05\x41\x44\x44\x45\x44\x10\x01\x12\x0b\n\x07REMOVED\x10\x02\x12\x0c\n\x08MODIFIED\x10\x03\x42q\n\x0e\x63om.google.apiB\x11\x43onfigChangeProtoP\x01ZCgoogle.golang.org/genproto/googleapis/api/configchange;configchange\xa2\x02\x04GAPIb\x06proto3'
)
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(
DESCRIPTOR, "google.api.config_change_pb2", _globals
)
if _descriptor._USE_C_DESCRIPTORS == False:
_globals["DESCRIPTOR"]._options = None
_globals[
"DESCRIPTOR"
]._serialized_options = b"\n\016com.google.apiB\021ConfigChangeProtoP\001ZCgoogle.golang.org/genproto/googleapis/api/configchange;configchange\242\002\004GAPI"
_globals["_CHANGETYPE"]._serialized_start = 231
_globals["_CHANGETYPE"]._serialized_end = 310
_globals["_CONFIGCHANGE"]._serialized_start = 47
_globals["_CONFIGCHANGE"]._serialized_end = 198
_globals["_ADVICE"]._serialized_start = 200
_globals["_ADVICE"]._serialized_end = 229
# @@protoc_insertion_point(module_scope)

View File

@@ -0,0 +1,65 @@
# Copyright 2025 Google LLC
#
# 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 typing import ClassVar as _ClassVar
from typing import Iterable as _Iterable
from typing import Mapping as _Mapping
from typing import Optional as _Optional
from typing import Union as _Union
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf.internal import containers as _containers
from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper
DESCRIPTOR: _descriptor.FileDescriptor
class ChangeType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
__slots__ = ()
CHANGE_TYPE_UNSPECIFIED: _ClassVar[ChangeType]
ADDED: _ClassVar[ChangeType]
REMOVED: _ClassVar[ChangeType]
MODIFIED: _ClassVar[ChangeType]
CHANGE_TYPE_UNSPECIFIED: ChangeType
ADDED: ChangeType
REMOVED: ChangeType
MODIFIED: ChangeType
class ConfigChange(_message.Message):
__slots__ = ("element", "old_value", "new_value", "change_type", "advices")
ELEMENT_FIELD_NUMBER: _ClassVar[int]
OLD_VALUE_FIELD_NUMBER: _ClassVar[int]
NEW_VALUE_FIELD_NUMBER: _ClassVar[int]
CHANGE_TYPE_FIELD_NUMBER: _ClassVar[int]
ADVICES_FIELD_NUMBER: _ClassVar[int]
element: str
old_value: str
new_value: str
change_type: ChangeType
advices: _containers.RepeatedCompositeFieldContainer[Advice]
def __init__(
self,
element: _Optional[str] = ...,
old_value: _Optional[str] = ...,
new_value: _Optional[str] = ...,
change_type: _Optional[_Union[ChangeType, str]] = ...,
advices: _Optional[_Iterable[_Union[Advice, _Mapping]]] = ...,
) -> None: ...
class Advice(_message.Message):
__slots__ = ("description",)
DESCRIPTION_FIELD_NUMBER: _ClassVar[int]
description: str
def __init__(self, description: _Optional[str] = ...) -> None: ...

View File

@@ -0,0 +1,82 @@
// Copyright 2025 Google LLC
//
// 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.
syntax = "proto3";
package google.api;
option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig";
option java_multiple_files = true;
option java_outer_classname = "ConsumerProto";
option java_package = "com.google.api";
// A descriptor for defining project properties for a service. One service may
// have many consumer projects, and the service may want to behave differently
// depending on some properties on the project. For example, a project may be
// associated with a school, or a business, or a government agency, a business
// type property on the project may affect how a service responds to the client.
// This descriptor defines which properties are allowed to be set on a project.
//
// Example:
//
// project_properties:
// properties:
// - name: NO_WATERMARK
// type: BOOL
// description: Allows usage of the API without watermarks.
// - name: EXTENDED_TILE_CACHE_PERIOD
// type: INT64
message ProjectProperties {
// List of per consumer project-specific properties.
repeated Property properties = 1;
}
// Defines project properties.
//
// API services can define properties that can be assigned to consumer projects
// so that backends can perform response customization without having to make
// additional calls or maintain additional storage. For example, Maps API
// defines properties that controls map tile cache period, or whether to embed a
// watermark in a result.
//
// These values can be set via API producer console. Only API providers can
// define and set these properties.
message Property {
// Supported data type of the property values
enum PropertyType {
// The type is unspecified, and will result in an error.
UNSPECIFIED = 0;
// The type is `int64`.
INT64 = 1;
// The type is `bool`.
BOOL = 2;
// The type is `string`.
STRING = 3;
// The type is 'double'.
DOUBLE = 4;
}
// The name of the property (a.k.a key).
string name = 1;
// The type of this property.
PropertyType type = 2;
// The description of the property
string description = 3;
}

View File

@@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-
# Copyright 2025 Google LLC
#
# 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.
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: google/api/consumer.proto
# Protobuf Python Version: 4.25.3
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
b'\n\x19google/api/consumer.proto\x12\ngoogle.api"=\n\x11ProjectProperties\x12(\n\nproperties\x18\x01 \x03(\x0b\x32\x14.google.api.Property"\xac\x01\n\x08Property\x12\x0c\n\x04name\x18\x01 \x01(\t\x12/\n\x04type\x18\x02 \x01(\x0e\x32!.google.api.Property.PropertyType\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t"L\n\x0cPropertyType\x12\x0f\n\x0bUNSPECIFIED\x10\x00\x12\t\n\x05INT64\x10\x01\x12\x08\n\x04\x42OOL\x10\x02\x12\n\n\x06STRING\x10\x03\x12\n\n\x06\x44OUBLE\x10\x04\x42h\n\x0e\x63om.google.apiB\rConsumerProtoP\x01ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfigb\x06proto3'
)
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.api.consumer_pb2", _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
_globals["DESCRIPTOR"]._options = None
_globals[
"DESCRIPTOR"
]._serialized_options = b"\n\016com.google.apiB\rConsumerProtoP\001ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig"
_globals["_PROJECTPROPERTIES"]._serialized_start = 41
_globals["_PROJECTPROPERTIES"]._serialized_end = 102
_globals["_PROPERTY"]._serialized_start = 105
_globals["_PROPERTY"]._serialized_end = 277
_globals["_PROPERTY_PROPERTYTYPE"]._serialized_start = 201
_globals["_PROPERTY_PROPERTYTYPE"]._serialized_end = 277
# @@protoc_insertion_point(module_scope)

View File

@@ -0,0 +1,62 @@
# Copyright 2025 Google LLC
#
# 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 typing import ClassVar as _ClassVar
from typing import Iterable as _Iterable
from typing import Mapping as _Mapping
from typing import Optional as _Optional
from typing import Union as _Union
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf.internal import containers as _containers
from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper
DESCRIPTOR: _descriptor.FileDescriptor
class ProjectProperties(_message.Message):
__slots__ = ("properties",)
PROPERTIES_FIELD_NUMBER: _ClassVar[int]
properties: _containers.RepeatedCompositeFieldContainer[Property]
def __init__(
self, properties: _Optional[_Iterable[_Union[Property, _Mapping]]] = ...
) -> None: ...
class Property(_message.Message):
__slots__ = ("name", "type", "description")
class PropertyType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
__slots__ = ()
UNSPECIFIED: _ClassVar[Property.PropertyType]
INT64: _ClassVar[Property.PropertyType]
BOOL: _ClassVar[Property.PropertyType]
STRING: _ClassVar[Property.PropertyType]
DOUBLE: _ClassVar[Property.PropertyType]
UNSPECIFIED: Property.PropertyType
INT64: Property.PropertyType
BOOL: Property.PropertyType
STRING: Property.PropertyType
DOUBLE: Property.PropertyType
NAME_FIELD_NUMBER: _ClassVar[int]
TYPE_FIELD_NUMBER: _ClassVar[int]
DESCRIPTION_FIELD_NUMBER: _ClassVar[int]
name: str
type: Property.PropertyType
description: str
def __init__(
self,
name: _Optional[str] = ...,
type: _Optional[_Union[Property.PropertyType, str]] = ...,
description: _Optional[str] = ...,
) -> None: ...

View File

@@ -0,0 +1,92 @@
// Copyright 2025 Google LLC
//
// 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.
syntax = "proto3";
package google.api;
option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig";
option java_multiple_files = true;
option java_outer_classname = "ContextProto";
option java_package = "com.google.api";
option objc_class_prefix = "GAPI";
// `Context` defines which contexts an API requests.
//
// Example:
//
// context:
// rules:
// - selector: "*"
// requested:
// - google.rpc.context.ProjectContext
// - google.rpc.context.OriginContext
//
// The above specifies that all methods in the API request
// `google.rpc.context.ProjectContext` and
// `google.rpc.context.OriginContext`.
//
// Available context types are defined in package
// `google.rpc.context`.
//
// This also provides mechanism to allowlist any protobuf message extension that
// can be sent in grpc metadata using “x-goog-ext-<extension_id>-bin” and
// “x-goog-ext-<extension_id>-jspb” format. For example, list any service
// specific protobuf types that can appear in grpc metadata as follows in your
// yaml file:
//
// Example:
//
// context:
// rules:
// - selector: "google.example.library.v1.LibraryService.CreateBook"
// allowed_request_extensions:
// - google.foo.v1.NewExtension
// allowed_response_extensions:
// - google.foo.v1.NewExtension
//
// You can also specify extension ID instead of fully qualified extension name
// here.
message Context {
// A list of RPC context rules that apply to individual API methods.
//
// **NOTE:** All service configuration rules follow "last one wins" order.
repeated ContextRule rules = 1;
}
// A context rule provides information about the context for an individual API
// element.
message ContextRule {
// Selects the methods to which this rule applies.
//
// Refer to [selector][google.api.DocumentationRule.selector] for syntax
// details.
string selector = 1;
// A list of full type names of requested contexts, only the requested context
// will be made available to the backend.
repeated string requested = 2;
// A list of full type names of provided contexts. It is used to support
// propagating HTTP headers and ETags from the response extension.
repeated string provided = 3;
// A list of full type names or extension IDs of extensions allowed in grpc
// side channel from client to backend.
repeated string allowed_request_extensions = 4;
// A list of full type names or extension IDs of extensions allowed in grpc
// side channel from backend to client.
repeated string allowed_response_extensions = 5;
}

View File

@@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
# Copyright 2025 Google LLC
#
# 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.
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: google/api/context.proto
# Protobuf Python Version: 4.25.3
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
b'\n\x18google/api/context.proto\x12\ngoogle.api"1\n\x07\x43ontext\x12&\n\x05rules\x18\x01 \x03(\x0b\x32\x17.google.api.ContextRule"\x8d\x01\n\x0b\x43ontextRule\x12\x10\n\x08selector\x18\x01 \x01(\t\x12\x11\n\trequested\x18\x02 \x03(\t\x12\x10\n\x08provided\x18\x03 \x03(\t\x12"\n\x1a\x61llowed_request_extensions\x18\x04 \x03(\t\x12#\n\x1b\x61llowed_response_extensions\x18\x05 \x03(\tBn\n\x0e\x63om.google.apiB\x0c\x43ontextProtoP\x01ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\xa2\x02\x04GAPIb\x06proto3'
)
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.api.context_pb2", _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
_globals["DESCRIPTOR"]._options = None
_globals[
"DESCRIPTOR"
]._serialized_options = b"\n\016com.google.apiB\014ContextProtoP\001ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\242\002\004GAPI"
_globals["_CONTEXT"]._serialized_start = 40
_globals["_CONTEXT"]._serialized_end = 89
_globals["_CONTEXTRULE"]._serialized_start = 92
_globals["_CONTEXTRULE"]._serialized_end = 233
# @@protoc_insertion_point(module_scope)

View File

@@ -0,0 +1,60 @@
# Copyright 2025 Google LLC
#
# 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 typing import ClassVar as _ClassVar
from typing import Iterable as _Iterable
from typing import Mapping as _Mapping
from typing import Optional as _Optional
from typing import Union as _Union
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf.internal import containers as _containers
DESCRIPTOR: _descriptor.FileDescriptor
class Context(_message.Message):
__slots__ = ("rules",)
RULES_FIELD_NUMBER: _ClassVar[int]
rules: _containers.RepeatedCompositeFieldContainer[ContextRule]
def __init__(
self, rules: _Optional[_Iterable[_Union[ContextRule, _Mapping]]] = ...
) -> None: ...
class ContextRule(_message.Message):
__slots__ = (
"selector",
"requested",
"provided",
"allowed_request_extensions",
"allowed_response_extensions",
)
SELECTOR_FIELD_NUMBER: _ClassVar[int]
REQUESTED_FIELD_NUMBER: _ClassVar[int]
PROVIDED_FIELD_NUMBER: _ClassVar[int]
ALLOWED_REQUEST_EXTENSIONS_FIELD_NUMBER: _ClassVar[int]
ALLOWED_RESPONSE_EXTENSIONS_FIELD_NUMBER: _ClassVar[int]
selector: str
requested: _containers.RepeatedScalarFieldContainer[str]
provided: _containers.RepeatedScalarFieldContainer[str]
allowed_request_extensions: _containers.RepeatedScalarFieldContainer[str]
allowed_response_extensions: _containers.RepeatedScalarFieldContainer[str]
def __init__(
self,
selector: _Optional[str] = ...,
requested: _Optional[_Iterable[str]] = ...,
provided: _Optional[_Iterable[str]] = ...,
allowed_request_extensions: _Optional[_Iterable[str]] = ...,
allowed_response_extensions: _Optional[_Iterable[str]] = ...,
) -> None: ...

View File

@@ -0,0 +1,41 @@
// Copyright 2025 Google LLC
//
// 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.
syntax = "proto3";
package google.api;
import "google/api/policy.proto";
option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig";
option java_multiple_files = true;
option java_outer_classname = "ControlProto";
option java_package = "com.google.api";
option objc_class_prefix = "GAPI";
// Selects and configures the service controller used by the service.
//
// Example:
//
// control:
// environment: servicecontrol.googleapis.com
message Control {
// The service controller environment to use. If empty, no control plane
// feature (like quota and billing) will be enabled. The recommended value for
// most services is servicecontrol.googleapis.com
string environment = 1;
// Defines policies applying to the API methods of the service.
repeated MethodPolicy method_policies = 4;
}

View File

@@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
# Copyright 2025 Google LLC
#
# 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.
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: google/api/control.proto
# Protobuf Python Version: 4.25.3
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
from google.api import policy_pb2 as google_dot_api_dot_policy__pb2
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
b'\n\x18google/api/control.proto\x12\ngoogle.api\x1a\x17google/api/policy.proto"Q\n\x07\x43ontrol\x12\x13\n\x0b\x65nvironment\x18\x01 \x01(\t\x12\x31\n\x0fmethod_policies\x18\x04 \x03(\x0b\x32\x18.google.api.MethodPolicyBn\n\x0e\x63om.google.apiB\x0c\x43ontrolProtoP\x01ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\xa2\x02\x04GAPIb\x06proto3'
)
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.api.control_pb2", _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
_globals["DESCRIPTOR"]._options = None
_globals[
"DESCRIPTOR"
]._serialized_options = b"\n\016com.google.apiB\014ControlProtoP\001ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\242\002\004GAPI"
_globals["_CONTROL"]._serialized_start = 65
_globals["_CONTROL"]._serialized_end = 146
# @@protoc_insertion_point(module_scope)

View File

@@ -0,0 +1,42 @@
# Copyright 2025 Google LLC
#
# 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 typing import ClassVar as _ClassVar
from typing import Iterable as _Iterable
from typing import Mapping as _Mapping
from typing import Optional as _Optional
from typing import Union as _Union
from google.api import policy_pb2 as _policy_pb2
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf.internal import containers as _containers
DESCRIPTOR: _descriptor.FileDescriptor
class Control(_message.Message):
__slots__ = ("environment", "method_policies")
ENVIRONMENT_FIELD_NUMBER: _ClassVar[int]
METHOD_POLICIES_FIELD_NUMBER: _ClassVar[int]
environment: str
method_policies: _containers.RepeatedCompositeFieldContainer[
_policy_pb2.MethodPolicy
]
def __init__(
self,
environment: _Optional[str] = ...,
method_policies: _Optional[
_Iterable[_Union[_policy_pb2.MethodPolicy, _Mapping]]
] = ...,
) -> None: ...

View File

@@ -0,0 +1,213 @@
// Copyright 2025 Google LLC
//
// 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.
syntax = "proto3";
package google.api;
import "google/protobuf/any.proto";
import "google/protobuf/timestamp.proto";
option go_package = "google.golang.org/genproto/googleapis/api/distribution;distribution";
option java_multiple_files = true;
option java_outer_classname = "DistributionProto";
option java_package = "com.google.api";
option objc_class_prefix = "GAPI";
// `Distribution` contains summary statistics for a population of values. It
// optionally contains a histogram representing the distribution of those values
// across a set of buckets.
//
// The summary statistics are the count, mean, sum of the squared deviation from
// the mean, the minimum, and the maximum of the set of population of values.
// The histogram is based on a sequence of buckets and gives a count of values
// that fall into each bucket. The boundaries of the buckets are given either
// explicitly or by formulas for buckets of fixed or exponentially increasing
// widths.
//
// Although it is not forbidden, it is generally a bad idea to include
// non-finite values (infinities or NaNs) in the population of values, as this
// will render the `mean` and `sum_of_squared_deviation` fields meaningless.
message Distribution {
// The range of the population values.
message Range {
// The minimum of the population values.
double min = 1;
// The maximum of the population values.
double max = 2;
}
// `BucketOptions` describes the bucket boundaries used to create a histogram
// for the distribution. The buckets can be in a linear sequence, an
// exponential sequence, or each bucket can be specified explicitly.
// `BucketOptions` does not include the number of values in each bucket.
//
// A bucket has an inclusive lower bound and exclusive upper bound for the
// values that are counted for that bucket. The upper bound of a bucket must
// be strictly greater than the lower bound. The sequence of N buckets for a
// distribution consists of an underflow bucket (number 0), zero or more
// finite buckets (number 1 through N - 2) and an overflow bucket (number N -
// 1). The buckets are contiguous: the lower bound of bucket i (i > 0) is the
// same as the upper bound of bucket i - 1. The buckets span the whole range
// of finite values: lower bound of the underflow bucket is -infinity and the
// upper bound of the overflow bucket is +infinity. The finite buckets are
// so-called because both bounds are finite.
message BucketOptions {
// Specifies a linear sequence of buckets that all have the same width
// (except overflow and underflow). Each bucket represents a constant
// absolute uncertainty on the specific value in the bucket.
//
// There are `num_finite_buckets + 2` (= N) buckets. Bucket `i` has the
// following boundaries:
//
// Upper bound (0 <= i < N-1): offset + (width * i).
//
// Lower bound (1 <= i < N): offset + (width * (i - 1)).
message Linear {
// Must be greater than 0.
int32 num_finite_buckets = 1;
// Must be greater than 0.
double width = 2;
// Lower bound of the first bucket.
double offset = 3;
}
// Specifies an exponential sequence of buckets that have a width that is
// proportional to the value of the lower bound. Each bucket represents a
// constant relative uncertainty on a specific value in the bucket.
//
// There are `num_finite_buckets + 2` (= N) buckets. Bucket `i` has the
// following boundaries:
//
// Upper bound (0 <= i < N-1): scale * (growth_factor ^ i).
//
// Lower bound (1 <= i < N): scale * (growth_factor ^ (i - 1)).
message Exponential {
// Must be greater than 0.
int32 num_finite_buckets = 1;
// Must be greater than 1.
double growth_factor = 2;
// Must be greater than 0.
double scale = 3;
}
// Specifies a set of buckets with arbitrary widths.
//
// There are `size(bounds) + 1` (= N) buckets. Bucket `i` has the following
// boundaries:
//
// Upper bound (0 <= i < N-1): bounds[i]
// Lower bound (1 <= i < N); bounds[i - 1]
//
// The `bounds` field must contain at least one element. If `bounds` has
// only one element, then there are no finite buckets, and that single
// element is the common boundary of the overflow and underflow buckets.
message Explicit {
// The values must be monotonically increasing.
repeated double bounds = 1;
}
// Exactly one of these three fields must be set.
oneof options {
// The linear bucket.
Linear linear_buckets = 1;
// The exponential buckets.
Exponential exponential_buckets = 2;
// The explicit buckets.
Explicit explicit_buckets = 3;
}
}
// Exemplars are example points that may be used to annotate aggregated
// distribution values. They are metadata that gives information about a
// particular value added to a Distribution bucket, such as a trace ID that
// was active when a value was added. They may contain further information,
// such as a example values and timestamps, origin, etc.
message Exemplar {
// Value of the exemplar point. This value determines to which bucket the
// exemplar belongs.
double value = 1;
// The observation (sampling) time of the above value.
google.protobuf.Timestamp timestamp = 2;
// Contextual information about the example value. Examples are:
//
// Trace: type.googleapis.com/google.monitoring.v3.SpanContext
//
// Literal string: type.googleapis.com/google.protobuf.StringValue
//
// Labels dropped during aggregation:
// type.googleapis.com/google.monitoring.v3.DroppedLabels
//
// There may be only a single attachment of any given message type in a
// single exemplar, and this is enforced by the system.
repeated google.protobuf.Any attachments = 3;
}
// The number of values in the population. Must be non-negative. This value
// must equal the sum of the values in `bucket_counts` if a histogram is
// provided.
int64 count = 1;
// The arithmetic mean of the values in the population. If `count` is zero
// then this field must be zero.
double mean = 2;
// The sum of squared deviations from the mean of the values in the
// population. For values x_i this is:
//
// Sum[i=1..n]((x_i - mean)^2)
//
// Knuth, "The Art of Computer Programming", Vol. 2, page 232, 3rd edition
// describes Welford's method for accumulating this sum in one pass.
//
// If `count` is zero then this field must be zero.
double sum_of_squared_deviation = 3;
// If specified, contains the range of the population values. The field
// must not be present if the `count` is zero.
Range range = 4;
// Defines the histogram bucket boundaries. If the distribution does not
// contain a histogram, then omit this field.
BucketOptions bucket_options = 6;
// The number of values in each bucket of the histogram, as described in
// `bucket_options`. If the distribution does not have a histogram, then omit
// this field. If there is a histogram, then the sum of the values in
// `bucket_counts` must equal the value in the `count` field of the
// distribution.
//
// If present, `bucket_counts` should contain N values, where N is the number
// of buckets specified in `bucket_options`. If you supply fewer than N
// values, the remaining values are assumed to be 0.
//
// The order of the values in `bucket_counts` follows the bucket numbering
// schemes described for the three bucket types. The first value must be the
// count for the underflow bucket (number 0). The next N-2 values are the
// counts for the finite buckets (number 1 through N-2). The N'th value in
// `bucket_counts` is the count for the overflow bucket (number N-1).
repeated int64 bucket_counts = 7;
// Must be in increasing order of `value` field.
repeated Exemplar exemplars = 10;
}

View File

@@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
# Copyright 2025 Google LLC
#
# 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.
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: google/api/distribution.proto
# Protobuf Python Version: 4.25.3
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2
from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
b'\n\x1dgoogle/api/distribution.proto\x12\ngoogle.api\x1a\x19google/protobuf/any.proto\x1a\x1fgoogle/protobuf/timestamp.proto"\xd9\x06\n\x0c\x44istribution\x12\r\n\x05\x63ount\x18\x01 \x01(\x03\x12\x0c\n\x04mean\x18\x02 \x01(\x01\x12 \n\x18sum_of_squared_deviation\x18\x03 \x01(\x01\x12-\n\x05range\x18\x04 \x01(\x0b\x32\x1e.google.api.Distribution.Range\x12>\n\x0e\x62ucket_options\x18\x06 \x01(\x0b\x32&.google.api.Distribution.BucketOptions\x12\x15\n\rbucket_counts\x18\x07 \x03(\x03\x12\x34\n\texemplars\x18\n \x03(\x0b\x32!.google.api.Distribution.Exemplar\x1a!\n\x05Range\x12\x0b\n\x03min\x18\x01 \x01(\x01\x12\x0b\n\x03max\x18\x02 \x01(\x01\x1a\xb5\x03\n\rBucketOptions\x12G\n\x0elinear_buckets\x18\x01 \x01(\x0b\x32-.google.api.Distribution.BucketOptions.LinearH\x00\x12Q\n\x13\x65xponential_buckets\x18\x02 \x01(\x0b\x32\x32.google.api.Distribution.BucketOptions.ExponentialH\x00\x12K\n\x10\x65xplicit_buckets\x18\x03 \x01(\x0b\x32/.google.api.Distribution.BucketOptions.ExplicitH\x00\x1a\x43\n\x06Linear\x12\x1a\n\x12num_finite_buckets\x18\x01 \x01(\x05\x12\r\n\x05width\x18\x02 \x01(\x01\x12\x0e\n\x06offset\x18\x03 \x01(\x01\x1aO\n\x0b\x45xponential\x12\x1a\n\x12num_finite_buckets\x18\x01 \x01(\x05\x12\x15\n\rgrowth_factor\x18\x02 \x01(\x01\x12\r\n\x05scale\x18\x03 \x01(\x01\x1a\x1a\n\x08\x45xplicit\x12\x0e\n\x06\x62ounds\x18\x01 \x03(\x01\x42\t\n\x07options\x1as\n\x08\x45xemplar\x12\r\n\x05value\x18\x01 \x01(\x01\x12-\n\ttimestamp\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12)\n\x0b\x61ttachments\x18\x03 \x03(\x0b\x32\x14.google.protobuf.AnyBq\n\x0e\x63om.google.apiB\x11\x44istributionProtoP\x01ZCgoogle.golang.org/genproto/googleapis/api/distribution;distribution\xa2\x02\x04GAPIb\x06proto3'
)
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(
DESCRIPTOR, "google.api.distribution_pb2", _globals
)
if _descriptor._USE_C_DESCRIPTORS == False:
_globals["DESCRIPTOR"]._options = None
_globals[
"DESCRIPTOR"
]._serialized_options = b"\n\016com.google.apiB\021DistributionProtoP\001ZCgoogle.golang.org/genproto/googleapis/api/distribution;distribution\242\002\004GAPI"
_globals["_DISTRIBUTION"]._serialized_start = 106
_globals["_DISTRIBUTION"]._serialized_end = 963
_globals["_DISTRIBUTION_RANGE"]._serialized_start = 373
_globals["_DISTRIBUTION_RANGE"]._serialized_end = 406
_globals["_DISTRIBUTION_BUCKETOPTIONS"]._serialized_start = 409
_globals["_DISTRIBUTION_BUCKETOPTIONS"]._serialized_end = 846
_globals["_DISTRIBUTION_BUCKETOPTIONS_LINEAR"]._serialized_start = 659
_globals["_DISTRIBUTION_BUCKETOPTIONS_LINEAR"]._serialized_end = 726
_globals["_DISTRIBUTION_BUCKETOPTIONS_EXPONENTIAL"]._serialized_start = 728
_globals["_DISTRIBUTION_BUCKETOPTIONS_EXPONENTIAL"]._serialized_end = 807
_globals["_DISTRIBUTION_BUCKETOPTIONS_EXPLICIT"]._serialized_start = 809
_globals["_DISTRIBUTION_BUCKETOPTIONS_EXPLICIT"]._serialized_end = 835
_globals["_DISTRIBUTION_EXEMPLAR"]._serialized_start = 848
_globals["_DISTRIBUTION_EXEMPLAR"]._serialized_end = 963
# @@protoc_insertion_point(module_scope)

View File

@@ -0,0 +1,144 @@
# Copyright 2025 Google LLC
#
# 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 typing import ClassVar as _ClassVar
from typing import Iterable as _Iterable
from typing import Mapping as _Mapping
from typing import Optional as _Optional
from typing import Union as _Union
from google.protobuf import any_pb2 as _any_pb2
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import timestamp_pb2 as _timestamp_pb2
from google.protobuf.internal import containers as _containers
DESCRIPTOR: _descriptor.FileDescriptor
class Distribution(_message.Message):
__slots__ = (
"count",
"mean",
"sum_of_squared_deviation",
"range",
"bucket_options",
"bucket_counts",
"exemplars",
)
class Range(_message.Message):
__slots__ = ("min", "max")
MIN_FIELD_NUMBER: _ClassVar[int]
MAX_FIELD_NUMBER: _ClassVar[int]
min: float
max: float
def __init__(
self, min: _Optional[float] = ..., max: _Optional[float] = ...
) -> None: ...
class BucketOptions(_message.Message):
__slots__ = ("linear_buckets", "exponential_buckets", "explicit_buckets")
class Linear(_message.Message):
__slots__ = ("num_finite_buckets", "width", "offset")
NUM_FINITE_BUCKETS_FIELD_NUMBER: _ClassVar[int]
WIDTH_FIELD_NUMBER: _ClassVar[int]
OFFSET_FIELD_NUMBER: _ClassVar[int]
num_finite_buckets: int
width: float
offset: float
def __init__(
self,
num_finite_buckets: _Optional[int] = ...,
width: _Optional[float] = ...,
offset: _Optional[float] = ...,
) -> None: ...
class Exponential(_message.Message):
__slots__ = ("num_finite_buckets", "growth_factor", "scale")
NUM_FINITE_BUCKETS_FIELD_NUMBER: _ClassVar[int]
GROWTH_FACTOR_FIELD_NUMBER: _ClassVar[int]
SCALE_FIELD_NUMBER: _ClassVar[int]
num_finite_buckets: int
growth_factor: float
scale: float
def __init__(
self,
num_finite_buckets: _Optional[int] = ...,
growth_factor: _Optional[float] = ...,
scale: _Optional[float] = ...,
) -> None: ...
class Explicit(_message.Message):
__slots__ = ("bounds",)
BOUNDS_FIELD_NUMBER: _ClassVar[int]
bounds: _containers.RepeatedScalarFieldContainer[float]
def __init__(self, bounds: _Optional[_Iterable[float]] = ...) -> None: ...
LINEAR_BUCKETS_FIELD_NUMBER: _ClassVar[int]
EXPONENTIAL_BUCKETS_FIELD_NUMBER: _ClassVar[int]
EXPLICIT_BUCKETS_FIELD_NUMBER: _ClassVar[int]
linear_buckets: Distribution.BucketOptions.Linear
exponential_buckets: Distribution.BucketOptions.Exponential
explicit_buckets: Distribution.BucketOptions.Explicit
def __init__(
self,
linear_buckets: _Optional[
_Union[Distribution.BucketOptions.Linear, _Mapping]
] = ...,
exponential_buckets: _Optional[
_Union[Distribution.BucketOptions.Exponential, _Mapping]
] = ...,
explicit_buckets: _Optional[
_Union[Distribution.BucketOptions.Explicit, _Mapping]
] = ...,
) -> None: ...
class Exemplar(_message.Message):
__slots__ = ("value", "timestamp", "attachments")
VALUE_FIELD_NUMBER: _ClassVar[int]
TIMESTAMP_FIELD_NUMBER: _ClassVar[int]
ATTACHMENTS_FIELD_NUMBER: _ClassVar[int]
value: float
timestamp: _timestamp_pb2.Timestamp
attachments: _containers.RepeatedCompositeFieldContainer[_any_pb2.Any]
def __init__(
self,
value: _Optional[float] = ...,
timestamp: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ...,
attachments: _Optional[_Iterable[_Union[_any_pb2.Any, _Mapping]]] = ...,
) -> None: ...
COUNT_FIELD_NUMBER: _ClassVar[int]
MEAN_FIELD_NUMBER: _ClassVar[int]
SUM_OF_SQUARED_DEVIATION_FIELD_NUMBER: _ClassVar[int]
RANGE_FIELD_NUMBER: _ClassVar[int]
BUCKET_OPTIONS_FIELD_NUMBER: _ClassVar[int]
BUCKET_COUNTS_FIELD_NUMBER: _ClassVar[int]
EXEMPLARS_FIELD_NUMBER: _ClassVar[int]
count: int
mean: float
sum_of_squared_deviation: float
range: Distribution.Range
bucket_options: Distribution.BucketOptions
bucket_counts: _containers.RepeatedScalarFieldContainer[int]
exemplars: _containers.RepeatedCompositeFieldContainer[Distribution.Exemplar]
def __init__(
self,
count: _Optional[int] = ...,
mean: _Optional[float] = ...,
sum_of_squared_deviation: _Optional[float] = ...,
range: _Optional[_Union[Distribution.Range, _Mapping]] = ...,
bucket_options: _Optional[_Union[Distribution.BucketOptions, _Mapping]] = ...,
bucket_counts: _Optional[_Iterable[int]] = ...,
exemplars: _Optional[_Iterable[_Union[Distribution.Exemplar, _Mapping]]] = ...,
) -> None: ...

View File

@@ -0,0 +1,168 @@
// Copyright 2025 Google LLC
//
// 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.
syntax = "proto3";
package google.api;
option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig";
option java_multiple_files = true;
option java_outer_classname = "DocumentationProto";
option java_package = "com.google.api";
option objc_class_prefix = "GAPI";
// `Documentation` provides the information for describing a service.
//
// Example:
// <pre><code>documentation:
// summary: >
// The Google Calendar API gives access
// to most calendar features.
// pages:
// - name: Overview
// content: &#40;== include google/foo/overview.md ==&#41;
// - name: Tutorial
// content: &#40;== include google/foo/tutorial.md ==&#41;
// subpages:
// - name: Java
// content: &#40;== include google/foo/tutorial_java.md ==&#41;
// rules:
// - selector: google.calendar.Calendar.Get
// description: >
// ...
// - selector: google.calendar.Calendar.Put
// description: >
// ...
// </code></pre>
// Documentation is provided in markdown syntax. In addition to
// standard markdown features, definition lists, tables and fenced
// code blocks are supported. Section headers can be provided and are
// interpreted relative to the section nesting of the context where
// a documentation fragment is embedded.
//
// Documentation from the IDL is merged with documentation defined
// via the config at normalization time, where documentation provided
// by config rules overrides IDL provided.
//
// A number of constructs specific to the API platform are supported
// in documentation text.
//
// In order to reference a proto element, the following
// notation can be used:
// <pre><code>&#91;fully.qualified.proto.name]&#91;]</code></pre>
// To override the display text used for the link, this can be used:
// <pre><code>&#91;display text]&#91;fully.qualified.proto.name]</code></pre>
// Text can be excluded from doc using the following notation:
// <pre><code>&#40;-- internal comment --&#41;</code></pre>
//
// A few directives are available in documentation. Note that
// directives must appear on a single line to be properly
// identified. The `include` directive includes a markdown file from
// an external source:
// <pre><code>&#40;== include path/to/file ==&#41;</code></pre>
// The `resource_for` directive marks a message to be the resource of
// a collection in REST view. If it is not specified, tools attempt
// to infer the resource from the operations in a collection:
// <pre><code>&#40;== resource_for v1.shelves.books ==&#41;</code></pre>
// The directive `suppress_warning` does not directly affect documentation
// and is documented together with service config validation.
message Documentation {
// A short description of what the service does. The summary must be plain
// text. It becomes the overview of the service displayed in Google Cloud
// Console.
// NOTE: This field is equivalent to the standard field `description`.
string summary = 1;
// The top level pages for the documentation set.
repeated Page pages = 5;
// A list of documentation rules that apply to individual API elements.
//
// **NOTE:** All service configuration rules follow "last one wins" order.
repeated DocumentationRule rules = 3;
// The URL to the root of documentation.
string documentation_root_url = 4;
// Specifies the service root url if the default one (the service name
// from the yaml file) is not suitable. This can be seen in any fully
// specified service urls as well as sections that show a base that other
// urls are relative to.
string service_root_url = 6;
// Declares a single overview page. For example:
// <pre><code>documentation:
// summary: ...
// overview: &#40;== include overview.md ==&#41;
// </code></pre>
// This is a shortcut for the following declaration (using pages style):
// <pre><code>documentation:
// summary: ...
// pages:
// - name: Overview
// content: &#40;== include overview.md ==&#41;
// </code></pre>
// Note: you cannot specify both `overview` field and `pages` field.
string overview = 2;
}
// A documentation rule provides information about individual API elements.
message DocumentationRule {
// The selector is a comma-separated list of patterns for any element such as
// a method, a field, an enum value. Each pattern is a qualified name of the
// element which may end in "*", indicating a wildcard. Wildcards are only
// allowed at the end and for a whole component of the qualified name,
// i.e. "foo.*" is ok, but not "foo.b*" or "foo.*.bar". A wildcard will match
// one or more components. To specify a default for all applicable elements,
// the whole pattern "*" is used.
string selector = 1;
// Description of the selected proto element (e.g. a message, a method, a
// 'service' definition, or a field). Defaults to leading & trailing comments
// taken from the proto source definition of the proto element.
string description = 2;
// Deprecation description of the selected element(s). It can be provided if
// an element is marked as `deprecated`.
string deprecation_description = 3;
}
// Represents a documentation page. A page can contain subpages to represent
// nested documentation set structure.
message Page {
// The name of the page. It will be used as an identity of the page to
// generate URI of the page, text of the link to this page in navigation,
// etc. The full page name (start from the root page name to this page
// concatenated with `.`) can be used as reference to the page in your
// documentation. For example:
// <pre><code>pages:
// - name: Tutorial
// content: &#40;== include tutorial.md ==&#41;
// subpages:
// - name: Java
// content: &#40;== include tutorial_java.md ==&#41;
// </code></pre>
// You can reference `Java` page using Markdown reference link syntax:
// `[Java][Tutorial.Java]`.
string name = 1;
// The Markdown content of the page. You can use ```(== include {path}
// ==)``` to include content from a Markdown file. The content can be used
// to produce the documentation page such as HTML format page.
string content = 2;
// Subpages of this page. The order of subpages specified here will be
// honored in the generated docset.
repeated Page subpages = 3;
}

View File

@@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
# Copyright 2025 Google LLC
#
# 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.
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: google/api/documentation.proto
# Protobuf Python Version: 4.25.3
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
b'\n\x1egoogle/api/documentation.proto\x12\ngoogle.api"\xbb\x01\n\rDocumentation\x12\x0f\n\x07summary\x18\x01 \x01(\t\x12\x1f\n\x05pages\x18\x05 \x03(\x0b\x32\x10.google.api.Page\x12,\n\x05rules\x18\x03 \x03(\x0b\x32\x1d.google.api.DocumentationRule\x12\x1e\n\x16\x64ocumentation_root_url\x18\x04 \x01(\t\x12\x18\n\x10service_root_url\x18\x06 \x01(\t\x12\x10\n\x08overview\x18\x02 \x01(\t"[\n\x11\x44ocumentationRule\x12\x10\n\x08selector\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x1f\n\x17\x64\x65precation_description\x18\x03 \x01(\t"I\n\x04Page\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x02 \x01(\t\x12"\n\x08subpages\x18\x03 \x03(\x0b\x32\x10.google.api.PageBt\n\x0e\x63om.google.apiB\x12\x44ocumentationProtoP\x01ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\xa2\x02\x04GAPIb\x06proto3'
)
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(
DESCRIPTOR, "google.api.documentation_pb2", _globals
)
if _descriptor._USE_C_DESCRIPTORS == False:
_globals["DESCRIPTOR"]._options = None
_globals[
"DESCRIPTOR"
]._serialized_options = b"\n\016com.google.apiB\022DocumentationProtoP\001ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\242\002\004GAPI"
_globals["_DOCUMENTATION"]._serialized_start = 47
_globals["_DOCUMENTATION"]._serialized_end = 234
_globals["_DOCUMENTATIONRULE"]._serialized_start = 236
_globals["_DOCUMENTATIONRULE"]._serialized_end = 327
_globals["_PAGE"]._serialized_start = 329
_globals["_PAGE"]._serialized_end = 402
# @@protoc_insertion_point(module_scope)

View File

@@ -0,0 +1,86 @@
# Copyright 2025 Google LLC
#
# 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 typing import ClassVar as _ClassVar
from typing import Iterable as _Iterable
from typing import Mapping as _Mapping
from typing import Optional as _Optional
from typing import Union as _Union
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf.internal import containers as _containers
DESCRIPTOR: _descriptor.FileDescriptor
class Documentation(_message.Message):
__slots__ = (
"summary",
"pages",
"rules",
"documentation_root_url",
"service_root_url",
"overview",
)
SUMMARY_FIELD_NUMBER: _ClassVar[int]
PAGES_FIELD_NUMBER: _ClassVar[int]
RULES_FIELD_NUMBER: _ClassVar[int]
DOCUMENTATION_ROOT_URL_FIELD_NUMBER: _ClassVar[int]
SERVICE_ROOT_URL_FIELD_NUMBER: _ClassVar[int]
OVERVIEW_FIELD_NUMBER: _ClassVar[int]
summary: str
pages: _containers.RepeatedCompositeFieldContainer[Page]
rules: _containers.RepeatedCompositeFieldContainer[DocumentationRule]
documentation_root_url: str
service_root_url: str
overview: str
def __init__(
self,
summary: _Optional[str] = ...,
pages: _Optional[_Iterable[_Union[Page, _Mapping]]] = ...,
rules: _Optional[_Iterable[_Union[DocumentationRule, _Mapping]]] = ...,
documentation_root_url: _Optional[str] = ...,
service_root_url: _Optional[str] = ...,
overview: _Optional[str] = ...,
) -> None: ...
class DocumentationRule(_message.Message):
__slots__ = ("selector", "description", "deprecation_description")
SELECTOR_FIELD_NUMBER: _ClassVar[int]
DESCRIPTION_FIELD_NUMBER: _ClassVar[int]
DEPRECATION_DESCRIPTION_FIELD_NUMBER: _ClassVar[int]
selector: str
description: str
deprecation_description: str
def __init__(
self,
selector: _Optional[str] = ...,
description: _Optional[str] = ...,
deprecation_description: _Optional[str] = ...,
) -> None: ...
class Page(_message.Message):
__slots__ = ("name", "content", "subpages")
NAME_FIELD_NUMBER: _ClassVar[int]
CONTENT_FIELD_NUMBER: _ClassVar[int]
SUBPAGES_FIELD_NUMBER: _ClassVar[int]
name: str
content: str
subpages: _containers.RepeatedCompositeFieldContainer[Page]
def __init__(
self,
name: _Optional[str] = ...,
content: _Optional[str] = ...,
subpages: _Optional[_Iterable[_Union[Page, _Mapping]]] = ...,
) -> None: ...

View File

@@ -0,0 +1,69 @@
// Copyright 2025 Google LLC
//
// 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.
syntax = "proto3";
package google.api;
option go_package = "google.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig";
option java_multiple_files = true;
option java_outer_classname = "EndpointProto";
option java_package = "com.google.api";
option objc_class_prefix = "GAPI";
// `Endpoint` describes a network address of a service that serves a set of
// APIs. It is commonly known as a service endpoint. A service may expose
// any number of service endpoints, and all service endpoints share the same
// service definition, such as quota limits and monitoring metrics.
//
// Example:
//
// type: google.api.Service
// name: library-example.googleapis.com
// endpoints:
// # Declares network address `https://library-example.googleapis.com`
// # for service `library-example.googleapis.com`. The `https` scheme
// # is implicit for all service endpoints. Other schemes may be
// # supported in the future.
// - name: library-example.googleapis.com
// allow_cors: false
// - name: content-staging-library-example.googleapis.com
// # Allows HTTP OPTIONS calls to be passed to the API frontend, for it
// # to decide whether the subsequent cross-origin request is allowed
// # to proceed.
// allow_cors: true
message Endpoint {
// The canonical name of this endpoint.
string name = 1;
// Aliases for this endpoint, these will be served by the same UrlMap as the
// parent endpoint, and will be provisioned in the GCP stack for the Regional
// Endpoints.
repeated string aliases = 2;
// The specification of an Internet routable address of API frontend that will
// handle requests to this [API
// Endpoint](https://cloud.google.com/apis/design/glossary). It should be
// either a valid IPv4 address or a fully-qualified domain name. For example,
// "8.8.8.8" or "myservice.appspot.com".
string target = 101;
// Allowing
// [CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing), aka
// cross-domain traffic, would allow the backends served from this endpoint to
// receive and respond to HTTP OPTIONS requests. The response will be used by
// the browser to determine whether the subsequent cross-origin request is
// allowed to proceed.
bool allow_cors = 5;
}

View File

@@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
# Copyright 2025 Google LLC
#
# 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.
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: google/api/endpoint.proto
# Protobuf Python Version: 4.25.3
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
b'\n\x19google/api/endpoint.proto\x12\ngoogle.api"M\n\x08\x45ndpoint\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07\x61liases\x18\x02 \x03(\t\x12\x0e\n\x06target\x18\x65 \x01(\t\x12\x12\n\nallow_cors\x18\x05 \x01(\x08\x42o\n\x0e\x63om.google.apiB\rEndpointProtoP\x01ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\xa2\x02\x04GAPIb\x06proto3'
)
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.api.endpoint_pb2", _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
_globals["DESCRIPTOR"]._options = None
_globals[
"DESCRIPTOR"
]._serialized_options = b"\n\016com.google.apiB\rEndpointProtoP\001ZEgoogle.golang.org/genproto/googleapis/api/serviceconfig;serviceconfig\242\002\004GAPI"
_globals["_ENDPOINT"]._serialized_start = 41
_globals["_ENDPOINT"]._serialized_end = 118
# @@protoc_insertion_point(module_scope)

View File

@@ -0,0 +1,41 @@
# Copyright 2025 Google LLC
#
# 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 typing import ClassVar as _ClassVar
from typing import Iterable as _Iterable
from typing import Optional as _Optional
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf.internal import containers as _containers
DESCRIPTOR: _descriptor.FileDescriptor
class Endpoint(_message.Message):
__slots__ = ("name", "aliases", "target", "allow_cors")
NAME_FIELD_NUMBER: _ClassVar[int]
ALIASES_FIELD_NUMBER: _ClassVar[int]
TARGET_FIELD_NUMBER: _ClassVar[int]
ALLOW_CORS_FIELD_NUMBER: _ClassVar[int]
name: str
aliases: _containers.RepeatedScalarFieldContainer[str]
target: str
allow_cors: bool
def __init__(
self,
name: _Optional[str] = ...,
aliases: _Optional[_Iterable[str]] = ...,
target: _Optional[str] = ...,
allow_cors: bool = ...,
) -> None: ...

View File

@@ -0,0 +1,622 @@
// Copyright 2025 Google LLC
//
// 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.
syntax = "proto3";
package google.api;
option go_package = "google.golang.org/genproto/googleapis/api/error_reason;error_reason";
option java_multiple_files = true;
option java_outer_classname = "ErrorReasonProto";
option java_package = "com.google.api";
option objc_class_prefix = "GAPI";
// Defines the supported values for `google.rpc.ErrorInfo.reason` for the
// `googleapis.com` error domain. This error domain is reserved for [Service
// Infrastructure](https://cloud.google.com/service-infrastructure/docs/overview).
// For each error info of this domain, the metadata key "service" refers to the
// logical identifier of an API service, such as "pubsub.googleapis.com". The
// "consumer" refers to the entity that consumes an API Service. It typically is
// a Google project that owns the client application or the server resource,
// such as "projects/123". Other metadata keys are specific to each error
// reason. For more information, see the definition of the specific error
// reason.
enum ErrorReason {
// Do not use this default value.
ERROR_REASON_UNSPECIFIED = 0;
// The request is calling a disabled service for a consumer.
//
// Example of an ErrorInfo when the consumer "projects/123" contacting
// "pubsub.googleapis.com" service which is disabled:
//
// { "reason": "SERVICE_DISABLED",
// "domain": "googleapis.com",
// "metadata": {
// "consumer": "projects/123",
// "service": "pubsub.googleapis.com"
// }
// }
//
// This response indicates the "pubsub.googleapis.com" has been disabled in
// "projects/123".
SERVICE_DISABLED = 1;
// The request whose associated billing account is disabled.
//
// Example of an ErrorInfo when the consumer "projects/123" fails to contact
// "pubsub.googleapis.com" service because the associated billing account is
// disabled:
//
// { "reason": "BILLING_DISABLED",
// "domain": "googleapis.com",
// "metadata": {
// "consumer": "projects/123",
// "service": "pubsub.googleapis.com"
// }
// }
//
// This response indicates the billing account associated has been disabled.
BILLING_DISABLED = 2;
// The request is denied because the provided [API
// key](https://cloud.google.com/docs/authentication/api-keys) is invalid. It
// may be in a bad format, cannot be found, or has been expired).
//
// Example of an ErrorInfo when the request is contacting
// "storage.googleapis.com" service with an invalid API key:
//
// { "reason": "API_KEY_INVALID",
// "domain": "googleapis.com",
// "metadata": {
// "service": "storage.googleapis.com",
// }
// }
API_KEY_INVALID = 3;
// The request is denied because it violates [API key API
// restrictions](https://cloud.google.com/docs/authentication/api-keys#adding_api_restrictions).
//
// Example of an ErrorInfo when the consumer "projects/123" fails to call the
// "storage.googleapis.com" service because this service is restricted in the
// API key:
//
// { "reason": "API_KEY_SERVICE_BLOCKED",
// "domain": "googleapis.com",
// "metadata": {
// "consumer": "projects/123",
// "service": "storage.googleapis.com"
// }
// }
API_KEY_SERVICE_BLOCKED = 4;
// The request is denied because it violates [API key HTTP
// restrictions](https://cloud.google.com/docs/authentication/api-keys#adding_http_restrictions).
//
// Example of an ErrorInfo when the consumer "projects/123" fails to call
// "storage.googleapis.com" service because the http referrer of the request
// violates API key HTTP restrictions:
//
// { "reason": "API_KEY_HTTP_REFERRER_BLOCKED",
// "domain": "googleapis.com",
// "metadata": {
// "consumer": "projects/123",
// "service": "storage.googleapis.com",
// }
// }
API_KEY_HTTP_REFERRER_BLOCKED = 7;
// The request is denied because it violates [API key IP address
// restrictions](https://cloud.google.com/docs/authentication/api-keys#adding_application_restrictions).
//
// Example of an ErrorInfo when the consumer "projects/123" fails to call
// "storage.googleapis.com" service because the caller IP of the request
// violates API key IP address restrictions:
//
// { "reason": "API_KEY_IP_ADDRESS_BLOCKED",
// "domain": "googleapis.com",
// "metadata": {
// "consumer": "projects/123",
// "service": "storage.googleapis.com",
// }
// }
API_KEY_IP_ADDRESS_BLOCKED = 8;
// The request is denied because it violates [API key Android application
// restrictions](https://cloud.google.com/docs/authentication/api-keys#adding_application_restrictions).
//
// Example of an ErrorInfo when the consumer "projects/123" fails to call
// "storage.googleapis.com" service because the request from the Android apps
// violates the API key Android application restrictions:
//
// { "reason": "API_KEY_ANDROID_APP_BLOCKED",
// "domain": "googleapis.com",
// "metadata": {
// "consumer": "projects/123",
// "service": "storage.googleapis.com"
// }
// }
API_KEY_ANDROID_APP_BLOCKED = 9;
// The request is denied because it violates [API key iOS application
// restrictions](https://cloud.google.com/docs/authentication/api-keys#adding_application_restrictions).
//
// Example of an ErrorInfo when the consumer "projects/123" fails to call
// "storage.googleapis.com" service because the request from the iOS apps
// violates the API key iOS application restrictions:
//
// { "reason": "API_KEY_IOS_APP_BLOCKED",
// "domain": "googleapis.com",
// "metadata": {
// "consumer": "projects/123",
// "service": "storage.googleapis.com"
// }
// }
API_KEY_IOS_APP_BLOCKED = 13;
// The request is denied because there is not enough rate quota for the
// consumer.
//
// Example of an ErrorInfo when the consumer "projects/123" fails to contact
// "pubsub.googleapis.com" service because consumer's rate quota usage has
// reached the maximum value set for the quota limit
// "ReadsPerMinutePerProject" on the quota metric
// "pubsub.googleapis.com/read_requests":
//
// { "reason": "RATE_LIMIT_EXCEEDED",
// "domain": "googleapis.com",
// "metadata": {
// "consumer": "projects/123",
// "service": "pubsub.googleapis.com",
// "quota_metric": "pubsub.googleapis.com/read_requests",
// "quota_limit": "ReadsPerMinutePerProject"
// }
// }
//
// Example of an ErrorInfo when the consumer "projects/123" checks quota on
// the service "dataflow.googleapis.com" and hits the organization quota
// limit "DefaultRequestsPerMinutePerOrganization" on the metric
// "dataflow.googleapis.com/default_requests".
//
// { "reason": "RATE_LIMIT_EXCEEDED",
// "domain": "googleapis.com",
// "metadata": {
// "consumer": "projects/123",
// "service": "dataflow.googleapis.com",
// "quota_metric": "dataflow.googleapis.com/default_requests",
// "quota_limit": "DefaultRequestsPerMinutePerOrganization"
// }
// }
RATE_LIMIT_EXCEEDED = 5;
// The request is denied because there is not enough resource quota for the
// consumer.
//
// Example of an ErrorInfo when the consumer "projects/123" fails to contact
// "compute.googleapis.com" service because consumer's resource quota usage
// has reached the maximum value set for the quota limit "VMsPerProject"
// on the quota metric "compute.googleapis.com/vms":
//
// { "reason": "RESOURCE_QUOTA_EXCEEDED",
// "domain": "googleapis.com",
// "metadata": {
// "consumer": "projects/123",
// "service": "compute.googleapis.com",
// "quota_metric": "compute.googleapis.com/vms",
// "quota_limit": "VMsPerProject"
// }
// }
//
// Example of an ErrorInfo when the consumer "projects/123" checks resource
// quota on the service "dataflow.googleapis.com" and hits the organization
// quota limit "jobs-per-organization" on the metric
// "dataflow.googleapis.com/job_count".
//
// { "reason": "RESOURCE_QUOTA_EXCEEDED",
// "domain": "googleapis.com",
// "metadata": {
// "consumer": "projects/123",
// "service": "dataflow.googleapis.com",
// "quota_metric": "dataflow.googleapis.com/job_count",
// "quota_limit": "jobs-per-organization"
// }
// }
RESOURCE_QUOTA_EXCEEDED = 6;
// The request whose associated billing account address is in a tax restricted
// location, violates the local tax restrictions when creating resources in
// the restricted region.
//
// Example of an ErrorInfo when creating the Cloud Storage Bucket in the
// container "projects/123" under a tax restricted region
// "locations/asia-northeast3":
//
// { "reason": "LOCATION_TAX_POLICY_VIOLATED",
// "domain": "googleapis.com",
// "metadata": {
// "consumer": "projects/123",
// "service": "storage.googleapis.com",
// "location": "locations/asia-northeast3"
// }
// }
//
// This response indicates creating the Cloud Storage Bucket in
// "locations/asia-northeast3" violates the location tax restriction.
LOCATION_TAX_POLICY_VIOLATED = 10;
// The request is denied because the caller does not have required permission
// on the user project "projects/123" or the user project is invalid. For more
// information, check the [userProject System
// Parameters](https://cloud.google.com/apis/docs/system-parameters).
//
// Example of an ErrorInfo when the caller is calling Cloud Storage service
// with insufficient permissions on the user project:
//
// { "reason": "USER_PROJECT_DENIED",
// "domain": "googleapis.com",
// "metadata": {
// "consumer": "projects/123",
// "service": "storage.googleapis.com"
// }
// }
USER_PROJECT_DENIED = 11;
// The request is denied because the consumer "projects/123" is suspended due
// to Terms of Service(Tos) violations. Check [Project suspension
// guidelines](https://cloud.google.com/resource-manager/docs/project-suspension-guidelines)
// for more information.
//
// Example of an ErrorInfo when calling Cloud Storage service with the
// suspended consumer "projects/123":
//
// { "reason": "CONSUMER_SUSPENDED",
// "domain": "googleapis.com",
// "metadata": {
// "consumer": "projects/123",
// "service": "storage.googleapis.com"
// }
// }
CONSUMER_SUSPENDED = 12;
// The request is denied because the associated consumer is invalid. It may be
// in a bad format, cannot be found, or have been deleted.
//
// Example of an ErrorInfo when calling Cloud Storage service with the
// invalid consumer "projects/123":
//
// { "reason": "CONSUMER_INVALID",
// "domain": "googleapis.com",
// "metadata": {
// "consumer": "projects/123",
// "service": "storage.googleapis.com"
// }
// }
CONSUMER_INVALID = 14;
// The request is denied because it violates [VPC Service
// Controls](https://cloud.google.com/vpc-service-controls/docs/overview).
// The 'uid' field is a random generated identifier that customer can use it
// to search the audit log for a request rejected by VPC Service Controls. For
// more information, please refer [VPC Service Controls
// Troubleshooting](https://cloud.google.com/vpc-service-controls/docs/troubleshooting#unique-id)
//
// Example of an ErrorInfo when the consumer "projects/123" fails to call
// Cloud Storage service because the request is prohibited by the VPC Service
// Controls.
//
// { "reason": "SECURITY_POLICY_VIOLATED",
// "domain": "googleapis.com",
// "metadata": {
// "uid": "123456789abcde",
// "consumer": "projects/123",
// "service": "storage.googleapis.com"
// }
// }
SECURITY_POLICY_VIOLATED = 15;
// The request is denied because the provided access token has expired.
//
// Example of an ErrorInfo when the request is calling Cloud Storage service
// with an expired access token:
//
// { "reason": "ACCESS_TOKEN_EXPIRED",
// "domain": "googleapis.com",
// "metadata": {
// "service": "storage.googleapis.com",
// "method": "google.storage.v1.Storage.GetObject"
// }
// }
ACCESS_TOKEN_EXPIRED = 16;
// The request is denied because the provided access token doesn't have at
// least one of the acceptable scopes required for the API. Please check
// [OAuth 2.0 Scopes for Google
// APIs](https://developers.google.com/identity/protocols/oauth2/scopes) for
// the list of the OAuth 2.0 scopes that you might need to request to access
// the API.
//
// Example of an ErrorInfo when the request is calling Cloud Storage service
// with an access token that is missing required scopes:
//
// { "reason": "ACCESS_TOKEN_SCOPE_INSUFFICIENT",
// "domain": "googleapis.com",
// "metadata": {
// "service": "storage.googleapis.com",
// "method": "google.storage.v1.Storage.GetObject"
// }
// }
ACCESS_TOKEN_SCOPE_INSUFFICIENT = 17;
// The request is denied because the account associated with the provided
// access token is in an invalid state, such as disabled or deleted.
// For more information, see https://cloud.google.com/docs/authentication.
//
// Warning: For privacy reasons, the server may not be able to disclose the
// email address for some accounts. The client MUST NOT depend on the
// availability of the `email` attribute.
//
// Example of an ErrorInfo when the request is to the Cloud Storage API with
// an access token that is associated with a disabled or deleted [service
// account](http://cloud/iam/docs/service-accounts):
//
// { "reason": "ACCOUNT_STATE_INVALID",
// "domain": "googleapis.com",
// "metadata": {
// "service": "storage.googleapis.com",
// "method": "google.storage.v1.Storage.GetObject",
// "email": "user@123.iam.gserviceaccount.com"
// }
// }
ACCOUNT_STATE_INVALID = 18;
// The request is denied because the type of the provided access token is not
// supported by the API being called.
//
// Example of an ErrorInfo when the request is to the Cloud Storage API with
// an unsupported token type.
//
// { "reason": "ACCESS_TOKEN_TYPE_UNSUPPORTED",
// "domain": "googleapis.com",
// "metadata": {
// "service": "storage.googleapis.com",
// "method": "google.storage.v1.Storage.GetObject"
// }
// }
ACCESS_TOKEN_TYPE_UNSUPPORTED = 19;
// The request is denied because the request doesn't have any authentication
// credentials. For more information regarding the supported authentication
// strategies for Google Cloud APIs, see
// https://cloud.google.com/docs/authentication.
//
// Example of an ErrorInfo when the request is to the Cloud Storage API
// without any authentication credentials.
//
// { "reason": "CREDENTIALS_MISSING",
// "domain": "googleapis.com",
// "metadata": {
// "service": "storage.googleapis.com",
// "method": "google.storage.v1.Storage.GetObject"
// }
// }
CREDENTIALS_MISSING = 20;
// The request is denied because the provided project owning the resource
// which acts as the [API
// consumer](https://cloud.google.com/apis/design/glossary#api_consumer) is
// invalid. It may be in a bad format or empty.
//
// Example of an ErrorInfo when the request is to the Cloud Functions API,
// but the offered resource project in the request in a bad format which can't
// perform the ListFunctions method.
//
// { "reason": "RESOURCE_PROJECT_INVALID",
// "domain": "googleapis.com",
// "metadata": {
// "service": "cloudfunctions.googleapis.com",
// "method":
// "google.cloud.functions.v1.CloudFunctionsService.ListFunctions"
// }
// }
RESOURCE_PROJECT_INVALID = 21;
// The request is denied because the provided session cookie is missing,
// invalid or failed to decode.
//
// Example of an ErrorInfo when the request is calling Cloud Storage service
// with a SID cookie which can't be decoded.
//
// { "reason": "SESSION_COOKIE_INVALID",
// "domain": "googleapis.com",
// "metadata": {
// "service": "storage.googleapis.com",
// "method": "google.storage.v1.Storage.GetObject",
// "cookie": "SID"
// }
// }
SESSION_COOKIE_INVALID = 23;
// The request is denied because the user is from a Google Workspace customer
// that blocks their users from accessing a particular service.
//
// Example scenario: https://support.google.com/a/answer/9197205?hl=en
//
// Example of an ErrorInfo when access to Google Cloud Storage service is
// blocked by the Google Workspace administrator:
//
// { "reason": "USER_BLOCKED_BY_ADMIN",
// "domain": "googleapis.com",
// "metadata": {
// "service": "storage.googleapis.com",
// "method": "google.storage.v1.Storage.GetObject",
// }
// }
USER_BLOCKED_BY_ADMIN = 24;
// The request is denied because the resource service usage is restricted
// by administrators according to the organization policy constraint.
// For more information see
// https://cloud.google.com/resource-manager/docs/organization-policy/restricting-services.
//
// Example of an ErrorInfo when access to Google Cloud Storage service is
// restricted by Resource Usage Restriction policy:
//
// { "reason": "RESOURCE_USAGE_RESTRICTION_VIOLATED",
// "domain": "googleapis.com",
// "metadata": {
// "consumer": "projects/project-123",
// "service": "storage.googleapis.com"
// }
// }
RESOURCE_USAGE_RESTRICTION_VIOLATED = 25;
// Unimplemented. Do not use.
//
// The request is denied because it contains unsupported system parameters in
// URL query parameters or HTTP headers. For more information,
// see https://cloud.google.com/apis/docs/system-parameters
//
// Example of an ErrorInfo when access "pubsub.googleapis.com" service with
// a request header of "x-goog-user-ip":
//
// { "reason": "SYSTEM_PARAMETER_UNSUPPORTED",
// "domain": "googleapis.com",
// "metadata": {
// "service": "pubsub.googleapis.com"
// "parameter": "x-goog-user-ip"
// }
// }
SYSTEM_PARAMETER_UNSUPPORTED = 26;
// The request is denied because it violates Org Restriction: the requested
// resource does not belong to allowed organizations specified in
// "X-Goog-Allowed-Resources" header.
//
// Example of an ErrorInfo when accessing a GCP resource that is restricted by
// Org Restriction for "pubsub.googleapis.com" service.
//
// {
// reason: "ORG_RESTRICTION_VIOLATION"
// domain: "googleapis.com"
// metadata {
// "consumer":"projects/123456"
// "service": "pubsub.googleapis.com"
// }
// }
ORG_RESTRICTION_VIOLATION = 27;
// The request is denied because "X-Goog-Allowed-Resources" header is in a bad
// format.
//
// Example of an ErrorInfo when
// accessing "pubsub.googleapis.com" service with an invalid
// "X-Goog-Allowed-Resources" request header.
//
// {
// reason: "ORG_RESTRICTION_HEADER_INVALID"
// domain: "googleapis.com"
// metadata {
// "consumer":"projects/123456"
// "service": "pubsub.googleapis.com"
// }
// }
ORG_RESTRICTION_HEADER_INVALID = 28;
// Unimplemented. Do not use.
//
// The request is calling a service that is not visible to the consumer.
//
// Example of an ErrorInfo when the consumer "projects/123" contacting
// "pubsub.googleapis.com" service which is not visible to the consumer.
//
// { "reason": "SERVICE_NOT_VISIBLE",
// "domain": "googleapis.com",
// "metadata": {
// "consumer": "projects/123",
// "service": "pubsub.googleapis.com"
// }
// }
//
// This response indicates the "pubsub.googleapis.com" is not visible to
// "projects/123" (or it may not exist).
SERVICE_NOT_VISIBLE = 29;
// The request is related to a project for which GCP access is suspended.
//
// Example of an ErrorInfo when the consumer "projects/123" fails to contact
// "pubsub.googleapis.com" service because GCP access is suspended:
//
// { "reason": "GCP_SUSPENDED",
// "domain": "googleapis.com",
// "metadata": {
// "consumer": "projects/123",
// "service": "pubsub.googleapis.com"
// }
// }
//
// This response indicates the associated GCP account has been suspended.
GCP_SUSPENDED = 30;
// The request violates the location policies when creating resources in
// the restricted region.
//
// Example of an ErrorInfo when creating the Cloud Storage Bucket by
// "projects/123" for service storage.googleapis.com:
//
// { "reason": "LOCATION_POLICY_VIOLATED",
// "domain": "googleapis.com",
// "metadata": {
// "consumer": "projects/123",
// "service": "storage.googleapis.com",
// }
// }
//
// This response indicates creating the Cloud Storage Bucket in
// "locations/asia-northeast3" violates at least one location policy.
// The troubleshooting guidance is provided in the Help links.
LOCATION_POLICY_VIOLATED = 31;
// The request is denied because origin request header is missing.
//
// Example of an ErrorInfo when
// accessing "pubsub.googleapis.com" service with an empty "Origin" request
// header.
//
// {
// reason: "MISSING_ORIGIN"
// domain: "googleapis.com"
// metadata {
// "consumer":"projects/123456"
// "service": "pubsub.googleapis.com"
// }
// }
MISSING_ORIGIN = 33;
// The request is denied because the request contains more than one credential
// type that are individually acceptable, but not together. The customer
// should retry their request with only one set of credentials.
//
// Example of an ErrorInfo when
// accessing "pubsub.googleapis.com" service with overloaded credentials.
//
// {
// reason: "OVERLOADED_CREDENTIALS"
// domain: "googleapis.com"
// metadata {
// "consumer":"projects/123456"
// "service": "pubsub.googleapis.com"
// }
// }
OVERLOADED_CREDENTIALS = 34;
}

View File

@@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
# Copyright 2025 Google LLC
#
# 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.
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: google/api/error_reason.proto
# Protobuf Python Version: 4.25.3
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
b'\n\x1dgoogle/api/error_reason.proto\x12\ngoogle.api*\xba\x07\n\x0b\x45rrorReason\x12\x1c\n\x18\x45RROR_REASON_UNSPECIFIED\x10\x00\x12\x14\n\x10SERVICE_DISABLED\x10\x01\x12\x14\n\x10\x42ILLING_DISABLED\x10\x02\x12\x13\n\x0f\x41PI_KEY_INVALID\x10\x03\x12\x1b\n\x17\x41PI_KEY_SERVICE_BLOCKED\x10\x04\x12!\n\x1d\x41PI_KEY_HTTP_REFERRER_BLOCKED\x10\x07\x12\x1e\n\x1a\x41PI_KEY_IP_ADDRESS_BLOCKED\x10\x08\x12\x1f\n\x1b\x41PI_KEY_ANDROID_APP_BLOCKED\x10\t\x12\x1b\n\x17\x41PI_KEY_IOS_APP_BLOCKED\x10\r\x12\x17\n\x13RATE_LIMIT_EXCEEDED\x10\x05\x12\x1b\n\x17RESOURCE_QUOTA_EXCEEDED\x10\x06\x12 \n\x1cLOCATION_TAX_POLICY_VIOLATED\x10\n\x12\x17\n\x13USER_PROJECT_DENIED\x10\x0b\x12\x16\n\x12\x43ONSUMER_SUSPENDED\x10\x0c\x12\x14\n\x10\x43ONSUMER_INVALID\x10\x0e\x12\x1c\n\x18SECURITY_POLICY_VIOLATED\x10\x0f\x12\x18\n\x14\x41\x43\x43\x45SS_TOKEN_EXPIRED\x10\x10\x12#\n\x1f\x41\x43\x43\x45SS_TOKEN_SCOPE_INSUFFICIENT\x10\x11\x12\x19\n\x15\x41\x43\x43OUNT_STATE_INVALID\x10\x12\x12!\n\x1d\x41\x43\x43\x45SS_TOKEN_TYPE_UNSUPPORTED\x10\x13\x12\x17\n\x13\x43REDENTIALS_MISSING\x10\x14\x12\x1c\n\x18RESOURCE_PROJECT_INVALID\x10\x15\x12\x1a\n\x16SESSION_COOKIE_INVALID\x10\x17\x12\x19\n\x15USER_BLOCKED_BY_ADMIN\x10\x18\x12\'\n#RESOURCE_USAGE_RESTRICTION_VIOLATED\x10\x19\x12 \n\x1cSYSTEM_PARAMETER_UNSUPPORTED\x10\x1a\x12\x1d\n\x19ORG_RESTRICTION_VIOLATION\x10\x1b\x12"\n\x1eORG_RESTRICTION_HEADER_INVALID\x10\x1c\x12\x17\n\x13SERVICE_NOT_VISIBLE\x10\x1d\x12\x11\n\rGCP_SUSPENDED\x10\x1e\x12\x1c\n\x18LOCATION_POLICY_VIOLATED\x10\x1f\x12\x12\n\x0eMISSING_ORIGIN\x10!\x12\x1a\n\x16OVERLOADED_CREDENTIALS\x10"Bp\n\x0e\x63om.google.apiB\x10\x45rrorReasonProtoP\x01ZCgoogle.golang.org/genproto/googleapis/api/error_reason;error_reason\xa2\x02\x04GAPIb\x06proto3'
)
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(
DESCRIPTOR, "google.api.error_reason_pb2", _globals
)
if _descriptor._USE_C_DESCRIPTORS == False:
_globals["DESCRIPTOR"]._options = None
_globals[
"DESCRIPTOR"
]._serialized_options = b"\n\016com.google.apiB\020ErrorReasonProtoP\001ZCgoogle.golang.org/genproto/googleapis/api/error_reason;error_reason\242\002\004GAPI"
_globals["_ERRORREASON"]._serialized_start = 46
_globals["_ERRORREASON"]._serialized_end = 1000
# @@protoc_insertion_point(module_scope)

View File

@@ -0,0 +1,90 @@
# Copyright 2025 Google LLC
#
# 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 typing import ClassVar as _ClassVar
from google.protobuf import descriptor as _descriptor
from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper
DESCRIPTOR: _descriptor.FileDescriptor
class ErrorReason(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
__slots__ = ()
ERROR_REASON_UNSPECIFIED: _ClassVar[ErrorReason]
SERVICE_DISABLED: _ClassVar[ErrorReason]
BILLING_DISABLED: _ClassVar[ErrorReason]
API_KEY_INVALID: _ClassVar[ErrorReason]
API_KEY_SERVICE_BLOCKED: _ClassVar[ErrorReason]
API_KEY_HTTP_REFERRER_BLOCKED: _ClassVar[ErrorReason]
API_KEY_IP_ADDRESS_BLOCKED: _ClassVar[ErrorReason]
API_KEY_ANDROID_APP_BLOCKED: _ClassVar[ErrorReason]
API_KEY_IOS_APP_BLOCKED: _ClassVar[ErrorReason]
RATE_LIMIT_EXCEEDED: _ClassVar[ErrorReason]
RESOURCE_QUOTA_EXCEEDED: _ClassVar[ErrorReason]
LOCATION_TAX_POLICY_VIOLATED: _ClassVar[ErrorReason]
USER_PROJECT_DENIED: _ClassVar[ErrorReason]
CONSUMER_SUSPENDED: _ClassVar[ErrorReason]
CONSUMER_INVALID: _ClassVar[ErrorReason]
SECURITY_POLICY_VIOLATED: _ClassVar[ErrorReason]
ACCESS_TOKEN_EXPIRED: _ClassVar[ErrorReason]
ACCESS_TOKEN_SCOPE_INSUFFICIENT: _ClassVar[ErrorReason]
ACCOUNT_STATE_INVALID: _ClassVar[ErrorReason]
ACCESS_TOKEN_TYPE_UNSUPPORTED: _ClassVar[ErrorReason]
CREDENTIALS_MISSING: _ClassVar[ErrorReason]
RESOURCE_PROJECT_INVALID: _ClassVar[ErrorReason]
SESSION_COOKIE_INVALID: _ClassVar[ErrorReason]
USER_BLOCKED_BY_ADMIN: _ClassVar[ErrorReason]
RESOURCE_USAGE_RESTRICTION_VIOLATED: _ClassVar[ErrorReason]
SYSTEM_PARAMETER_UNSUPPORTED: _ClassVar[ErrorReason]
ORG_RESTRICTION_VIOLATION: _ClassVar[ErrorReason]
ORG_RESTRICTION_HEADER_INVALID: _ClassVar[ErrorReason]
SERVICE_NOT_VISIBLE: _ClassVar[ErrorReason]
GCP_SUSPENDED: _ClassVar[ErrorReason]
LOCATION_POLICY_VIOLATED: _ClassVar[ErrorReason]
MISSING_ORIGIN: _ClassVar[ErrorReason]
OVERLOADED_CREDENTIALS: _ClassVar[ErrorReason]
ERROR_REASON_UNSPECIFIED: ErrorReason
SERVICE_DISABLED: ErrorReason
BILLING_DISABLED: ErrorReason
API_KEY_INVALID: ErrorReason
API_KEY_SERVICE_BLOCKED: ErrorReason
API_KEY_HTTP_REFERRER_BLOCKED: ErrorReason
API_KEY_IP_ADDRESS_BLOCKED: ErrorReason
API_KEY_ANDROID_APP_BLOCKED: ErrorReason
API_KEY_IOS_APP_BLOCKED: ErrorReason
RATE_LIMIT_EXCEEDED: ErrorReason
RESOURCE_QUOTA_EXCEEDED: ErrorReason
LOCATION_TAX_POLICY_VIOLATED: ErrorReason
USER_PROJECT_DENIED: ErrorReason
CONSUMER_SUSPENDED: ErrorReason
CONSUMER_INVALID: ErrorReason
SECURITY_POLICY_VIOLATED: ErrorReason
ACCESS_TOKEN_EXPIRED: ErrorReason
ACCESS_TOKEN_SCOPE_INSUFFICIENT: ErrorReason
ACCOUNT_STATE_INVALID: ErrorReason
ACCESS_TOKEN_TYPE_UNSUPPORTED: ErrorReason
CREDENTIALS_MISSING: ErrorReason
RESOURCE_PROJECT_INVALID: ErrorReason
SESSION_COOKIE_INVALID: ErrorReason
USER_BLOCKED_BY_ADMIN: ErrorReason
RESOURCE_USAGE_RESTRICTION_VIOLATED: ErrorReason
SYSTEM_PARAMETER_UNSUPPORTED: ErrorReason
ORG_RESTRICTION_VIOLATION: ErrorReason
ORG_RESTRICTION_HEADER_INVALID: ErrorReason
SERVICE_NOT_VISIBLE: ErrorReason
GCP_SUSPENDED: ErrorReason
LOCATION_POLICY_VIOLATED: ErrorReason
MISSING_ORIGIN: ErrorReason
OVERLOADED_CREDENTIALS: ErrorReason

View File

@@ -0,0 +1,104 @@
// Copyright 2025 Google LLC
//
// 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.
syntax = "proto3";
package google.api;
import "google/protobuf/descriptor.proto";
option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
option java_multiple_files = true;
option java_outer_classname = "FieldBehaviorProto";
option java_package = "com.google.api";
option objc_class_prefix = "GAPI";
extend google.protobuf.FieldOptions {
// A designation of a specific field behavior (required, output only, etc.)
// in protobuf messages.
//
// Examples:
//
// string name = 1 [(google.api.field_behavior) = REQUIRED];
// State state = 1 [(google.api.field_behavior) = OUTPUT_ONLY];
// google.protobuf.Duration ttl = 1
// [(google.api.field_behavior) = INPUT_ONLY];
// google.protobuf.Timestamp expire_time = 1
// [(google.api.field_behavior) = OUTPUT_ONLY,
// (google.api.field_behavior) = IMMUTABLE];
repeated google.api.FieldBehavior field_behavior = 1052 [packed = false];
}
// An indicator of the behavior of a given field (for example, that a field
// is required in requests, or given as output but ignored as input).
// This **does not** change the behavior in protocol buffers itself; it only
// denotes the behavior and may affect how API tooling handles the field.
//
// Note: This enum **may** receive new values in the future.
enum FieldBehavior {
// Conventional default for enums. Do not use this.
FIELD_BEHAVIOR_UNSPECIFIED = 0;
// Specifically denotes a field as optional.
// While all fields in protocol buffers are optional, this may be specified
// for emphasis if appropriate.
OPTIONAL = 1;
// Denotes a field as required.
// This indicates that the field **must** be provided as part of the request,
// and failure to do so will cause an error (usually `INVALID_ARGUMENT`).
REQUIRED = 2;
// Denotes a field as output only.
// This indicates that the field is provided in responses, but including the
// field in a request does nothing (the server *must* ignore it and
// *must not* throw an error as a result of the field's presence).
OUTPUT_ONLY = 3;
// Denotes a field as input only.
// This indicates that the field is provided in requests, and the
// corresponding field is not included in output.
INPUT_ONLY = 4;
// Denotes a field as immutable.
// This indicates that the field may be set once in a request to create a
// resource, but may not be changed thereafter.
IMMUTABLE = 5;
// Denotes that a (repeated) field is an unordered list.
// This indicates that the service may provide the elements of the list
// in any arbitrary order, rather than the order the user originally
// provided. Additionally, the list's order may or may not be stable.
UNORDERED_LIST = 6;
// Denotes that this field returns a non-empty default value if not set.
// This indicates that if the user provides the empty value in a request,
// a non-empty value will be returned. The user will not be aware of what
// non-empty value to expect.
NON_EMPTY_DEFAULT = 7;
// Denotes that the field in a resource (a message annotated with
// google.api.resource) is used in the resource name to uniquely identify the
// resource. For AIP-compliant APIs, this should only be applied to the
// `name` field on the resource.
//
// This behavior should not be applied to references to other resources within
// the message.
//
// The identifier field of resources often have different field behavior
// depending on the request it is embedded in (e.g. for Create methods name
// is optional and unused, while for Update methods it is required). Instead
// of method-specific annotations, only `IDENTIFIER` is required.
IDENTIFIER = 8;
}

View File

@@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
# Copyright 2025 Google LLC
#
# 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.
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: google/api/field_behavior.proto
# Protobuf Python Version: 4.25.3
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
b"\n\x1fgoogle/api/field_behavior.proto\x12\ngoogle.api\x1a google/protobuf/descriptor.proto*\xb6\x01\n\rFieldBehavior\x12\x1e\n\x1a\x46IELD_BEHAVIOR_UNSPECIFIED\x10\x00\x12\x0c\n\x08OPTIONAL\x10\x01\x12\x0c\n\x08REQUIRED\x10\x02\x12\x0f\n\x0bOUTPUT_ONLY\x10\x03\x12\x0e\n\nINPUT_ONLY\x10\x04\x12\r\n\tIMMUTABLE\x10\x05\x12\x12\n\x0eUNORDERED_LIST\x10\x06\x12\x15\n\x11NON_EMPTY_DEFAULT\x10\x07\x12\x0e\n\nIDENTIFIER\x10\x08:U\n\x0e\x66ield_behavior\x12\x1d.google.protobuf.FieldOptions\x18\x9c\x08 \x03(\x0e\x32\x19.google.api.FieldBehaviorB\x02\x10\x00\x42p\n\x0e\x63om.google.apiB\x12\x46ieldBehaviorProtoP\x01ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\xa2\x02\x04GAPIb\x06proto3"
)
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(
DESCRIPTOR, "google.api.field_behavior_pb2", _globals
)
if _descriptor._USE_C_DESCRIPTORS == False:
_globals["DESCRIPTOR"]._options = None
_globals[
"DESCRIPTOR"
]._serialized_options = b"\n\016com.google.apiB\022FieldBehaviorProtoP\001ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\242\002\004GAPI"
_globals["field_behavior"]._options = None
_globals["field_behavior"]._serialized_options = b"\020\000"
_globals["_FIELDBEHAVIOR"]._serialized_start = 82
_globals["_FIELDBEHAVIOR"]._serialized_end = 264
# @@protoc_insertion_point(module_scope)

View File

@@ -0,0 +1,45 @@
# Copyright 2025 Google LLC
#
# 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 typing import ClassVar as _ClassVar
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pb2 as _descriptor_pb2
from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper
DESCRIPTOR: _descriptor.FileDescriptor
class FieldBehavior(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
__slots__ = ()
FIELD_BEHAVIOR_UNSPECIFIED: _ClassVar[FieldBehavior]
OPTIONAL: _ClassVar[FieldBehavior]
REQUIRED: _ClassVar[FieldBehavior]
OUTPUT_ONLY: _ClassVar[FieldBehavior]
INPUT_ONLY: _ClassVar[FieldBehavior]
IMMUTABLE: _ClassVar[FieldBehavior]
UNORDERED_LIST: _ClassVar[FieldBehavior]
NON_EMPTY_DEFAULT: _ClassVar[FieldBehavior]
IDENTIFIER: _ClassVar[FieldBehavior]
FIELD_BEHAVIOR_UNSPECIFIED: FieldBehavior
OPTIONAL: FieldBehavior
REQUIRED: FieldBehavior
OUTPUT_ONLY: FieldBehavior
INPUT_ONLY: FieldBehavior
IMMUTABLE: FieldBehavior
UNORDERED_LIST: FieldBehavior
NON_EMPTY_DEFAULT: FieldBehavior
IDENTIFIER: FieldBehavior
FIELD_BEHAVIOR_FIELD_NUMBER: _ClassVar[int]
field_behavior: _descriptor.FieldDescriptor

View File

@@ -0,0 +1,106 @@
// Copyright 2025 Google LLC
//
// 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.
syntax = "proto3";
package google.api;
import "google/protobuf/descriptor.proto";
option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
option java_multiple_files = true;
option java_outer_classname = "FieldInfoProto";
option java_package = "com.google.api";
option objc_class_prefix = "GAPI";
extend google.protobuf.FieldOptions {
// Rich semantic descriptor of an API field beyond the basic typing.
//
// Examples:
//
// string request_id = 1 [(google.api.field_info).format = UUID4];
// string old_ip_address = 2 [(google.api.field_info).format = IPV4];
// string new_ip_address = 3 [(google.api.field_info).format = IPV6];
// string actual_ip_address = 4 [
// (google.api.field_info).format = IPV4_OR_IPV6
// ];
// google.protobuf.Any generic_field = 5 [
// (google.api.field_info).referenced_types = {type_name: "ActualType"},
// (google.api.field_info).referenced_types = {type_name: "OtherType"},
// ];
// google.protobuf.Any generic_user_input = 5 [
// (google.api.field_info).referenced_types = {type_name: "*"},
// ];
google.api.FieldInfo field_info = 291403980;
}
// Rich semantic information of an API field beyond basic typing.
message FieldInfo {
// The standard format of a field value. The supported formats are all backed
// by either an RFC defined by the IETF or a Google-defined AIP.
enum Format {
// Default, unspecified value.
FORMAT_UNSPECIFIED = 0;
// Universally Unique Identifier, version 4, value as defined by
// https://datatracker.ietf.org/doc/html/rfc4122. The value may be
// normalized to entirely lowercase letters. For example, the value
// `F47AC10B-58CC-0372-8567-0E02B2C3D479` would be normalized to
// `f47ac10b-58cc-0372-8567-0e02b2c3d479`.
UUID4 = 1;
// Internet Protocol v4 value as defined by [RFC
// 791](https://datatracker.ietf.org/doc/html/rfc791). The value may be
// condensed, with leading zeros in each octet stripped. For example,
// `001.022.233.040` would be condensed to `1.22.233.40`.
IPV4 = 2;
// Internet Protocol v6 value as defined by [RFC
// 2460](https://datatracker.ietf.org/doc/html/rfc2460). The value may be
// normalized to entirely lowercase letters with zeros compressed, following
// [RFC 5952](https://datatracker.ietf.org/doc/html/rfc5952). For example,
// the value `2001:0DB8:0::0` would be normalized to `2001:db8::`.
IPV6 = 3;
// An IP address in either v4 or v6 format as described by the individual
// values defined herein. See the comments on the IPV4 and IPV6 types for
// allowed normalizations of each.
IPV4_OR_IPV6 = 4;
}
// The standard format of a field value. This does not explicitly configure
// any API consumer, just documents the API's format for the field it is
// applied to.
Format format = 1;
// The type(s) that the annotated, generic field may represent.
//
// Currently, this must only be used on fields of type `google.protobuf.Any`.
// Supporting other generic types may be considered in the future.
repeated TypeReference referenced_types = 2;
}
// A reference to a message type, for use in [FieldInfo][google.api.FieldInfo].
message TypeReference {
// The name of the type that the annotated, generic field may represent.
// If the type is in the same protobuf package, the value can be the simple
// message name e.g., `"MyMessage"`. Otherwise, the value must be the
// fully-qualified message name e.g., `"google.library.v1.Book"`.
//
// If the type(s) are unknown to the service (e.g. the field accepts generic
// user input), use the wildcard `"*"` to denote this behavior.
//
// See [AIP-202](https://google.aip.dev/202#type-references) for more details.
string type_name = 1;
}

View File

@@ -0,0 +1,53 @@
# -*- coding: utf-8 -*-
# Copyright 2025 Google LLC
#
# 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.
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: google/api/field_info.proto
# Protobuf Python Version: 4.25.3
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
b'\n\x1bgoogle/api/field_info.proto\x12\ngoogle.api\x1a google/protobuf/descriptor.proto"\xc1\x01\n\tFieldInfo\x12,\n\x06\x66ormat\x18\x01 \x01(\x0e\x32\x1c.google.api.FieldInfo.Format\x12\x33\n\x10referenced_types\x18\x02 \x03(\x0b\x32\x19.google.api.TypeReference"Q\n\x06\x46ormat\x12\x16\n\x12\x46ORMAT_UNSPECIFIED\x10\x00\x12\t\n\x05UUID4\x10\x01\x12\x08\n\x04IPV4\x10\x02\x12\x08\n\x04IPV6\x10\x03\x12\x10\n\x0cIPV4_OR_IPV6\x10\x04""\n\rTypeReference\x12\x11\n\ttype_name\x18\x01 \x01(\t:L\n\nfield_info\x12\x1d.google.protobuf.FieldOptions\x18\xcc\xf1\xf9\x8a\x01 \x01(\x0b\x32\x15.google.api.FieldInfoBl\n\x0e\x63om.google.apiB\x0e\x46ieldInfoProtoP\x01ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\xa2\x02\x04GAPIb\x06proto3'
)
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(
DESCRIPTOR, "google.api.field_info_pb2", _globals
)
if _descriptor._USE_C_DESCRIPTORS == False:
_globals["DESCRIPTOR"]._options = None
_globals[
"DESCRIPTOR"
]._serialized_options = b"\n\016com.google.apiB\016FieldInfoProtoP\001ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\242\002\004GAPI"
_globals["_FIELDINFO"]._serialized_start = 78
_globals["_FIELDINFO"]._serialized_end = 271
_globals["_FIELDINFO_FORMAT"]._serialized_start = 190
_globals["_FIELDINFO_FORMAT"]._serialized_end = 271
_globals["_TYPEREFERENCE"]._serialized_start = 273
_globals["_TYPEREFERENCE"]._serialized_end = 307
# @@protoc_insertion_point(module_scope)

View File

@@ -0,0 +1,60 @@
# Copyright 2025 Google LLC
#
# 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 typing import ClassVar as _ClassVar
from typing import Iterable as _Iterable
from typing import Mapping as _Mapping
from typing import Optional as _Optional
from typing import Union as _Union
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pb2 as _descriptor_pb2
from google.protobuf import message as _message
from google.protobuf.internal import containers as _containers
from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper
DESCRIPTOR: _descriptor.FileDescriptor
FIELD_INFO_FIELD_NUMBER: _ClassVar[int]
field_info: _descriptor.FieldDescriptor
class FieldInfo(_message.Message):
__slots__ = ("format", "referenced_types")
class Format(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
__slots__ = ()
FORMAT_UNSPECIFIED: _ClassVar[FieldInfo.Format]
UUID4: _ClassVar[FieldInfo.Format]
IPV4: _ClassVar[FieldInfo.Format]
IPV6: _ClassVar[FieldInfo.Format]
IPV4_OR_IPV6: _ClassVar[FieldInfo.Format]
FORMAT_UNSPECIFIED: FieldInfo.Format
UUID4: FieldInfo.Format
IPV4: FieldInfo.Format
IPV6: FieldInfo.Format
IPV4_OR_IPV6: FieldInfo.Format
FORMAT_FIELD_NUMBER: _ClassVar[int]
REFERENCED_TYPES_FIELD_NUMBER: _ClassVar[int]
format: FieldInfo.Format
referenced_types: _containers.RepeatedCompositeFieldContainer[TypeReference]
def __init__(
self,
format: _Optional[_Union[FieldInfo.Format, str]] = ...,
referenced_types: _Optional[_Iterable[_Union[TypeReference, _Mapping]]] = ...,
) -> None: ...
class TypeReference(_message.Message):
__slots__ = ("type_name",)
TYPE_NAME_FIELD_NUMBER: _ClassVar[int]
type_name: str
def __init__(self, type_name: _Optional[str] = ...) -> None: ...

View File

@@ -0,0 +1,370 @@
// Copyright 2025 Google LLC
//
// 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.
syntax = "proto3";
package google.api;
option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations";
option java_multiple_files = true;
option java_outer_classname = "HttpProto";
option java_package = "com.google.api";
option objc_class_prefix = "GAPI";
// Defines the HTTP configuration for an API service. It contains a list of
// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method
// to one or more HTTP REST API methods.
message Http {
// A list of HTTP configuration rules that apply to individual API methods.
//
// **NOTE:** All service configuration rules follow "last one wins" order.
repeated HttpRule rules = 1;
// When set to true, URL path parameters will be fully URI-decoded except in
// cases of single segment matches in reserved expansion, where "%2F" will be
// left encoded.
//
// The default behavior is to not decode RFC 6570 reserved characters in multi
// segment matches.
bool fully_decode_reserved_expansion = 2;
}
// gRPC Transcoding
//
// gRPC Transcoding is a feature for mapping between a gRPC method and one or
// more HTTP REST endpoints. It allows developers to build a single API service
// that supports both gRPC APIs and REST APIs. Many systems, including [Google
// APIs](https://github.com/googleapis/googleapis),
// [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC
// Gateway](https://github.com/grpc-ecosystem/grpc-gateway),
// and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature
// and use it for large scale production services.
//
// `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies
// how different portions of the gRPC request message are mapped to the URL
// path, URL query parameters, and HTTP request body. It also controls how the
// gRPC response message is mapped to the HTTP response body. `HttpRule` is
// typically specified as an `google.api.http` annotation on the gRPC method.
//
// Each mapping specifies a URL path template and an HTTP method. The path
// template may refer to one or more fields in the gRPC request message, as long
// as each field is a non-repeated field with a primitive (non-message) type.
// The path template controls how fields of the request message are mapped to
// the URL path.
//
// Example:
//
// service Messaging {
// rpc GetMessage(GetMessageRequest) returns (Message) {
// option (google.api.http) = {
// get: "/v1/{name=messages/*}"
// };
// }
// }
// message GetMessageRequest {
// string name = 1; // Mapped to URL path.
// }
// message Message {
// string text = 1; // The resource content.
// }
//
// This enables an HTTP REST to gRPC mapping as below:
//
// - HTTP: `GET /v1/messages/123456`
// - gRPC: `GetMessage(name: "messages/123456")`
//
// Any fields in the request message which are not bound by the path template
// automatically become HTTP query parameters if there is no HTTP request body.
// For example:
//
// service Messaging {
// rpc GetMessage(GetMessageRequest) returns (Message) {
// option (google.api.http) = {
// get:"/v1/messages/{message_id}"
// };
// }
// }
// message GetMessageRequest {
// message SubMessage {
// string subfield = 1;
// }
// string message_id = 1; // Mapped to URL path.
// int64 revision = 2; // Mapped to URL query parameter `revision`.
// SubMessage sub = 3; // Mapped to URL query parameter `sub.subfield`.
// }
//
// This enables a HTTP JSON to RPC mapping as below:
//
// - HTTP: `GET /v1/messages/123456?revision=2&sub.subfield=foo`
// - gRPC: `GetMessage(message_id: "123456" revision: 2 sub:
// SubMessage(subfield: "foo"))`
//
// Note that fields which are mapped to URL query parameters must have a
// primitive type or a repeated primitive type or a non-repeated message type.
// In the case of a repeated type, the parameter can be repeated in the URL
// as `...?param=A&param=B`. In the case of a message type, each field of the
// message is mapped to a separate parameter, such as
// `...?foo.a=A&foo.b=B&foo.c=C`.
//
// For HTTP methods that allow a request body, the `body` field
// specifies the mapping. Consider a REST update method on the
// message resource collection:
//
// service Messaging {
// rpc UpdateMessage(UpdateMessageRequest) returns (Message) {
// option (google.api.http) = {
// patch: "/v1/messages/{message_id}"
// body: "message"
// };
// }
// }
// message UpdateMessageRequest {
// string message_id = 1; // mapped to the URL
// Message message = 2; // mapped to the body
// }
//
// The following HTTP JSON to RPC mapping is enabled, where the
// representation of the JSON in the request body is determined by
// protos JSON encoding:
//
// - HTTP: `PATCH /v1/messages/123456 { "text": "Hi!" }`
// - gRPC: `UpdateMessage(message_id: "123456" message { text: "Hi!" })`
//
// The special name `*` can be used in the body mapping to define that
// every field not bound by the path template should be mapped to the
// request body. This enables the following alternative definition of
// the update method:
//
// service Messaging {
// rpc UpdateMessage(Message) returns (Message) {
// option (google.api.http) = {
// patch: "/v1/messages/{message_id}"
// body: "*"
// };
// }
// }
// message Message {
// string message_id = 1;
// string text = 2;
// }
//
//
// The following HTTP JSON to RPC mapping is enabled:
//
// - HTTP: `PATCH /v1/messages/123456 { "text": "Hi!" }`
// - gRPC: `UpdateMessage(message_id: "123456" text: "Hi!")`
//
// Note that when using `*` in the body mapping, it is not possible to
// have HTTP parameters, as all fields not bound by the path end in
// the body. This makes this option more rarely used in practice when
// defining REST APIs. The common usage of `*` is in custom methods
// which don't use the URL at all for transferring data.
//
// It is possible to define multiple HTTP methods for one RPC by using
// the `additional_bindings` option. Example:
//
// service Messaging {
// rpc GetMessage(GetMessageRequest) returns (Message) {
// option (google.api.http) = {
// get: "/v1/messages/{message_id}"
// additional_bindings {
// get: "/v1/users/{user_id}/messages/{message_id}"
// }
// };
// }
// }
// message GetMessageRequest {
// string message_id = 1;
// string user_id = 2;
// }
//
// This enables the following two alternative HTTP JSON to RPC mappings:
//
// - HTTP: `GET /v1/messages/123456`
// - gRPC: `GetMessage(message_id: "123456")`
//
// - HTTP: `GET /v1/users/me/messages/123456`
// - gRPC: `GetMessage(user_id: "me" message_id: "123456")`
//
// Rules for HTTP mapping
//
// 1. Leaf request fields (recursive expansion nested messages in the request
// message) are classified into three categories:
// - Fields referred by the path template. They are passed via the URL path.
// - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They
// are passed via the HTTP
// request body.
// - All other fields are passed via the URL query parameters, and the
// parameter name is the field path in the request message. A repeated
// field can be represented as multiple query parameters under the same
// name.
// 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL
// query parameter, all fields
// are passed via URL path and HTTP request body.
// 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP
// request body, all
// fields are passed via URL path and URL query parameters.
//
// Path template syntax
//
// Template = "/" Segments [ Verb ] ;
// Segments = Segment { "/" Segment } ;
// Segment = "*" | "**" | LITERAL | Variable ;
// Variable = "{" FieldPath [ "=" Segments ] "}" ;
// FieldPath = IDENT { "." IDENT } ;
// Verb = ":" LITERAL ;
//
// The syntax `*` matches a single URL path segment. The syntax `**` matches
// zero or more URL path segments, which must be the last part of the URL path
// except the `Verb`.
//
// The syntax `Variable` matches part of the URL path as specified by its
// template. A variable template must not contain other variables. If a variable
// matches a single path segment, its template may be omitted, e.g. `{var}`
// is equivalent to `{var=*}`.
//
// The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL`
// contains any reserved character, such characters should be percent-encoded
// before the matching.
//
// If a variable contains exactly one path segment, such as `"{var}"` or
// `"{var=*}"`, when such a variable is expanded into a URL path on the client
// side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The
// server side does the reverse decoding. Such variables show up in the
// [Discovery
// Document](https://developers.google.com/discovery/v1/reference/apis) as
// `{var}`.
//
// If a variable contains multiple path segments, such as `"{var=foo/*}"`
// or `"{var=**}"`, when such a variable is expanded into a URL path on the
// client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded.
// The server side does the reverse decoding, except "%2F" and "%2f" are left
// unchanged. Such variables show up in the
// [Discovery
// Document](https://developers.google.com/discovery/v1/reference/apis) as
// `{+var}`.
//
// Using gRPC API Service Configuration
//
// gRPC API Service Configuration (service config) is a configuration language
// for configuring a gRPC service to become a user-facing product. The
// service config is simply the YAML representation of the `google.api.Service`
// proto message.
//
// As an alternative to annotating your proto file, you can configure gRPC
// transcoding in your service config YAML files. You do this by specifying a
// `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same
// effect as the proto annotation. This can be particularly useful if you
// have a proto that is reused in multiple services. Note that any transcoding
// specified in the service config will override any matching transcoding
// configuration in the proto.
//
// The following example selects a gRPC method and applies an `HttpRule` to it:
//
// http:
// rules:
// - selector: example.v1.Messaging.GetMessage
// get: /v1/messages/{message_id}/{sub.subfield}
//
// Special notes
//
// When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the
// proto to JSON conversion must follow the [proto3
// specification](https://developers.google.com/protocol-buffers/docs/proto3#json).
//
// While the single segment variable follows the semantics of
// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String
// Expansion, the multi segment variable **does not** follow RFC 6570 Section
// 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion
// does not expand special characters like `?` and `#`, which would lead
// to invalid URLs. As the result, gRPC Transcoding uses a custom encoding
// for multi segment variables.
//
// The path variables **must not** refer to any repeated or mapped field,
// because client libraries are not capable of handling such variable expansion.
//
// The path variables **must not** capture the leading "/" character. The reason
// is that the most common use case "{var}" does not capture the leading "/"
// character. For consistency, all path variables must share the same behavior.
//
// Repeated message fields must not be mapped to URL query parameters, because
// no client library can support such complicated mapping.
//
// If an API needs to use a JSON array for request or response body, it can map
// the request or response body to a repeated field. However, some gRPC
// Transcoding implementations may not support this feature.
message HttpRule {
// Selects a method to which this rule applies.
//
// Refer to [selector][google.api.DocumentationRule.selector] for syntax
// details.
string selector = 1;
// Determines the URL pattern is matched by this rules. This pattern can be
// used with any of the {get|put|post|delete|patch} methods. A custom method
// can be defined using the 'custom' field.
oneof pattern {
// Maps to HTTP GET. Used for listing and getting information about
// resources.
string get = 2;
// Maps to HTTP PUT. Used for replacing a resource.
string put = 3;
// Maps to HTTP POST. Used for creating a resource or performing an action.
string post = 4;
// Maps to HTTP DELETE. Used for deleting a resource.
string delete = 5;
// Maps to HTTP PATCH. Used for updating a resource.
string patch = 6;
// The custom pattern is used for specifying an HTTP method that is not
// included in the `pattern` field, such as HEAD, or "*" to leave the
// HTTP method unspecified for this rule. The wild-card rule is useful
// for services that provide content to Web (HTML) clients.
CustomHttpPattern custom = 8;
}
// The name of the request field whose value is mapped to the HTTP request
// body, or `*` for mapping all request fields not captured by the path
// pattern to the HTTP body, or omitted for not having any HTTP request body.
//
// NOTE: the referred field must be present at the top-level of the request
// message type.
string body = 7;
// Optional. The name of the response field whose value is mapped to the HTTP
// response body. When omitted, the entire response message will be used
// as the HTTP response body.
//
// NOTE: The referred field must be present at the top-level of the response
// message type.
string response_body = 12;
// Additional HTTP bindings for the selector. Nested bindings must
// not contain an `additional_bindings` field themselves (that is,
// the nesting may only be one level deep).
repeated HttpRule additional_bindings = 11;
}
// A custom pattern is used for defining custom HTTP verb.
message CustomHttpPattern {
// The name of this custom HTTP verb.
string kind = 1;
// The path matched by this custom verb.
string path = 2;
}

View File

@@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-
# Copyright 2025 Google LLC
#
# 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.
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: google/api/http.proto
# Protobuf Python Version: 4.25.3
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
b'\n\x15google/api/http.proto\x12\ngoogle.api"T\n\x04Http\x12#\n\x05rules\x18\x01 \x03(\x0b\x32\x14.google.api.HttpRule\x12\'\n\x1f\x66ully_decode_reserved_expansion\x18\x02 \x01(\x08"\x81\x02\n\x08HttpRule\x12\x10\n\x08selector\x18\x01 \x01(\t\x12\r\n\x03get\x18\x02 \x01(\tH\x00\x12\r\n\x03put\x18\x03 \x01(\tH\x00\x12\x0e\n\x04post\x18\x04 \x01(\tH\x00\x12\x10\n\x06\x64\x65lete\x18\x05 \x01(\tH\x00\x12\x0f\n\x05patch\x18\x06 \x01(\tH\x00\x12/\n\x06\x63ustom\x18\x08 \x01(\x0b\x32\x1d.google.api.CustomHttpPatternH\x00\x12\x0c\n\x04\x62ody\x18\x07 \x01(\t\x12\x15\n\rresponse_body\x18\x0c \x01(\t\x12\x31\n\x13\x61\x64\x64itional_bindings\x18\x0b \x03(\x0b\x32\x14.google.api.HttpRuleB\t\n\x07pattern"/\n\x11\x43ustomHttpPattern\x12\x0c\n\x04kind\x18\x01 \x01(\t\x12\x0c\n\x04path\x18\x02 \x01(\tBg\n\x0e\x63om.google.apiB\tHttpProtoP\x01ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\xa2\x02\x04GAPIb\x06proto3'
)
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.api.http_pb2", _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
_globals["DESCRIPTOR"]._options = None
_globals[
"DESCRIPTOR"
]._serialized_options = b"\n\016com.google.apiB\tHttpProtoP\001ZAgoogle.golang.org/genproto/googleapis/api/annotations;annotations\242\002\004GAPI"
_globals["_HTTP"]._serialized_start = 37
_globals["_HTTP"]._serialized_end = 121
_globals["_HTTPRULE"]._serialized_start = 124
_globals["_HTTPRULE"]._serialized_end = 381
_globals["_CUSTOMHTTPPATTERN"]._serialized_start = 383
_globals["_CUSTOMHTTPPATTERN"]._serialized_end = 430
# @@protoc_insertion_point(module_scope)

View File

@@ -0,0 +1,94 @@
# Copyright 2025 Google LLC
#
# 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 typing import ClassVar as _ClassVar
from typing import Iterable as _Iterable
from typing import Mapping as _Mapping
from typing import Optional as _Optional
from typing import Union as _Union
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf.internal import containers as _containers
DESCRIPTOR: _descriptor.FileDescriptor
class Http(_message.Message):
__slots__ = ("rules", "fully_decode_reserved_expansion")
RULES_FIELD_NUMBER: _ClassVar[int]
FULLY_DECODE_RESERVED_EXPANSION_FIELD_NUMBER: _ClassVar[int]
rules: _containers.RepeatedCompositeFieldContainer[HttpRule]
fully_decode_reserved_expansion: bool
def __init__(
self,
rules: _Optional[_Iterable[_Union[HttpRule, _Mapping]]] = ...,
fully_decode_reserved_expansion: bool = ...,
) -> None: ...
class HttpRule(_message.Message):
__slots__ = (
"selector",
"get",
"put",
"post",
"delete",
"patch",
"custom",
"body",
"response_body",
"additional_bindings",
)
SELECTOR_FIELD_NUMBER: _ClassVar[int]
GET_FIELD_NUMBER: _ClassVar[int]
PUT_FIELD_NUMBER: _ClassVar[int]
POST_FIELD_NUMBER: _ClassVar[int]
DELETE_FIELD_NUMBER: _ClassVar[int]
PATCH_FIELD_NUMBER: _ClassVar[int]
CUSTOM_FIELD_NUMBER: _ClassVar[int]
BODY_FIELD_NUMBER: _ClassVar[int]
RESPONSE_BODY_FIELD_NUMBER: _ClassVar[int]
ADDITIONAL_BINDINGS_FIELD_NUMBER: _ClassVar[int]
selector: str
get: str
put: str
post: str
delete: str
patch: str
custom: CustomHttpPattern
body: str
response_body: str
additional_bindings: _containers.RepeatedCompositeFieldContainer[HttpRule]
def __init__(
self,
selector: _Optional[str] = ...,
get: _Optional[str] = ...,
put: _Optional[str] = ...,
post: _Optional[str] = ...,
delete: _Optional[str] = ...,
patch: _Optional[str] = ...,
custom: _Optional[_Union[CustomHttpPattern, _Mapping]] = ...,
body: _Optional[str] = ...,
response_body: _Optional[str] = ...,
additional_bindings: _Optional[_Iterable[_Union[HttpRule, _Mapping]]] = ...,
) -> None: ...
class CustomHttpPattern(_message.Message):
__slots__ = ("kind", "path")
KIND_FIELD_NUMBER: _ClassVar[int]
PATH_FIELD_NUMBER: _ClassVar[int]
kind: str
path: str
def __init__(
self, kind: _Optional[str] = ..., path: _Optional[str] = ...
) -> None: ...

View File

@@ -0,0 +1,80 @@
// Copyright 2025 Google LLC
//
// 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.
syntax = "proto3";
package google.api;
import "google/protobuf/any.proto";
option go_package = "google.golang.org/genproto/googleapis/api/httpbody;httpbody";
option java_multiple_files = true;
option java_outer_classname = "HttpBodyProto";
option java_package = "com.google.api";
option objc_class_prefix = "GAPI";
// Message that represents an arbitrary HTTP body. It should only be used for
// payload formats that can't be represented as JSON, such as raw binary or
// an HTML page.
//
//
// This message can be used both in streaming and non-streaming API methods in
// the request as well as the response.
//
// It can be used as a top-level request field, which is convenient if one
// wants to extract parameters from either the URL or HTTP template into the
// request fields and also want access to the raw HTTP body.
//
// Example:
//
// message GetResourceRequest {
// // A unique request id.
// string request_id = 1;
//
// // The raw HTTP body is bound to this field.
// google.api.HttpBody http_body = 2;
//
// }
//
// service ResourceService {
// rpc GetResource(GetResourceRequest)
// returns (google.api.HttpBody);
// rpc UpdateResource(google.api.HttpBody)
// returns (google.protobuf.Empty);
//
// }
//
// Example with streaming methods:
//
// service CaldavService {
// rpc GetCalendar(stream google.api.HttpBody)
// returns (stream google.api.HttpBody);
// rpc UpdateCalendar(stream google.api.HttpBody)
// returns (stream google.api.HttpBody);
//
// }
//
// Use of this type only changes how the request and response bodies are
// handled, all other features will continue to work unchanged.
message HttpBody {
// The HTTP Content-Type header value specifying the content type of the body.
string content_type = 1;
// The HTTP request/response body as raw binary.
bytes data = 2;
// Application specific response metadata. Must be set in the first response
// for streaming APIs.
repeated google.protobuf.Any extensions = 3;
}

View File

@@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
# Copyright 2025 Google LLC
#
# 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.
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: google/api/httpbody.proto
# Protobuf Python Version: 4.25.3
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
b'\n\x19google/api/httpbody.proto\x12\ngoogle.api\x1a\x19google/protobuf/any.proto"X\n\x08HttpBody\x12\x14\n\x0c\x63ontent_type\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12(\n\nextensions\x18\x03 \x03(\x0b\x32\x14.google.protobuf.AnyBe\n\x0e\x63om.google.apiB\rHttpBodyProtoP\x01Z;google.golang.org/genproto/googleapis/api/httpbody;httpbody\xa2\x02\x04GAPIb\x06proto3'
)
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "google.api.httpbody_pb2", _globals)
if _descriptor._USE_C_DESCRIPTORS == False:
_globals["DESCRIPTOR"]._options = None
_globals[
"DESCRIPTOR"
]._serialized_options = b"\n\016com.google.apiB\rHttpBodyProtoP\001Z;google.golang.org/genproto/googleapis/api/httpbody;httpbody\242\002\004GAPI"
_globals["_HTTPBODY"]._serialized_start = 68
_globals["_HTTPBODY"]._serialized_end = 156
# @@protoc_insertion_point(module_scope)

Some files were not shown because too many files have changed in this diff Show More