1# -*- coding: utf-8 -*-
2# Copyright 2022 Google LLC
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16from collections import OrderedDict
17import os
18import re
19from typing import (
20 Dict,
21 Mapping,
22 MutableMapping,
23 MutableSequence,
24 Optional,
25 Sequence,
26 Tuple,
27 Type,
28 Union,
29 cast,
30)
31
32from google.cloud.errorreporting_v1beta1 import gapic_version as package_version
33
34from google.api_core import client_options as client_options_lib
35from google.api_core import exceptions as core_exceptions
36from google.api_core import gapic_v1
37from google.api_core import retry as retries
38from google.auth import credentials as ga_credentials # type: ignore
39from google.auth.transport import mtls # type: ignore
40from google.auth.transport.grpc import SslCredentials # type: ignore
41from google.auth.exceptions import MutualTLSChannelError # type: ignore
42from google.oauth2 import service_account # type: ignore
43
44try:
45 OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault]
46except AttributeError: # pragma: NO COVER
47 OptionalRetry = Union[retries.Retry, object] # type: ignore
48
49from google.cloud.errorreporting_v1beta1.types import report_errors_service
50from .transports.base import ReportErrorsServiceTransport, DEFAULT_CLIENT_INFO
51from .transports.grpc import ReportErrorsServiceGrpcTransport
52from .transports.grpc_asyncio import ReportErrorsServiceGrpcAsyncIOTransport
53from .transports.rest import ReportErrorsServiceRestTransport
54
55
56class ReportErrorsServiceClientMeta(type):
57 """Metaclass for the ReportErrorsService client.
58
59 This provides class-level methods for building and retrieving
60 support objects (e.g. transport) without polluting the client instance
61 objects.
62 """
63
64 _transport_registry = (
65 OrderedDict()
66 ) # type: Dict[str, Type[ReportErrorsServiceTransport]]
67 _transport_registry["grpc"] = ReportErrorsServiceGrpcTransport
68 _transport_registry["grpc_asyncio"] = ReportErrorsServiceGrpcAsyncIOTransport
69 _transport_registry["rest"] = ReportErrorsServiceRestTransport
70
71 def get_transport_class(
72 cls,
73 label: Optional[str] = None,
74 ) -> Type[ReportErrorsServiceTransport]:
75 """Returns an appropriate transport class.
76
77 Args:
78 label: The name of the desired transport. If none is
79 provided, then the first transport in the registry is used.
80
81 Returns:
82 The transport class to use.
83 """
84 # If a specific transport is requested, return that one.
85 if label:
86 return cls._transport_registry[label]
87
88 # No transport is requested; return the default (that is, the first one
89 # in the dictionary).
90 return next(iter(cls._transport_registry.values()))
91
92
93class ReportErrorsServiceClient(metaclass=ReportErrorsServiceClientMeta):
94 """An API for reporting error events."""
95
96 @staticmethod
97 def _get_default_mtls_endpoint(api_endpoint):
98 """Converts api endpoint to mTLS endpoint.
99
100 Convert "*.sandbox.googleapis.com" and "*.googleapis.com" to
101 "*.mtls.sandbox.googleapis.com" and "*.mtls.googleapis.com" respectively.
102 Args:
103 api_endpoint (Optional[str]): the api endpoint to convert.
104 Returns:
105 str: converted mTLS api endpoint.
106 """
107 if not api_endpoint:
108 return api_endpoint
109
110 mtls_endpoint_re = re.compile(
111 r"(?P<name>[^.]+)(?P<mtls>\.mtls)?(?P<sandbox>\.sandbox)?(?P<googledomain>\.googleapis\.com)?"
112 )
113
114 m = mtls_endpoint_re.match(api_endpoint)
115 name, mtls, sandbox, googledomain = m.groups()
116 if mtls or not googledomain:
117 return api_endpoint
118
119 if sandbox:
120 return api_endpoint.replace(
121 "sandbox.googleapis.com", "mtls.sandbox.googleapis.com"
122 )
123
124 return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com")
125
126 DEFAULT_ENDPOINT = "clouderrorreporting.googleapis.com"
127 DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore
128 DEFAULT_ENDPOINT
129 )
130
131 @classmethod
132 def from_service_account_info(cls, info: dict, *args, **kwargs):
133 """Creates an instance of this client using the provided credentials
134 info.
135
136 Args:
137 info (dict): The service account private key info.
138 args: Additional arguments to pass to the constructor.
139 kwargs: Additional arguments to pass to the constructor.
140
141 Returns:
142 ReportErrorsServiceClient: The constructed client.
143 """
144 credentials = service_account.Credentials.from_service_account_info(info)
145 kwargs["credentials"] = credentials
146 return cls(*args, **kwargs)
147
148 @classmethod
149 def from_service_account_file(cls, filename: str, *args, **kwargs):
150 """Creates an instance of this client using the provided credentials
151 file.
152
153 Args:
154 filename (str): The path to the service account private key json
155 file.
156 args: Additional arguments to pass to the constructor.
157 kwargs: Additional arguments to pass to the constructor.
158
159 Returns:
160 ReportErrorsServiceClient: The constructed client.
161 """
162 credentials = service_account.Credentials.from_service_account_file(filename)
163 kwargs["credentials"] = credentials
164 return cls(*args, **kwargs)
165
166 from_service_account_json = from_service_account_file
167
168 @property
169 def transport(self) -> ReportErrorsServiceTransport:
170 """Returns the transport used by the client instance.
171
172 Returns:
173 ReportErrorsServiceTransport: The transport used by the client
174 instance.
175 """
176 return self._transport
177
178 @staticmethod
179 def common_billing_account_path(
180 billing_account: str,
181 ) -> str:
182 """Returns a fully-qualified billing_account string."""
183 return "billingAccounts/{billing_account}".format(
184 billing_account=billing_account,
185 )
186
187 @staticmethod
188 def parse_common_billing_account_path(path: str) -> Dict[str, str]:
189 """Parse a billing_account path into its component segments."""
190 m = re.match(r"^billingAccounts/(?P<billing_account>.+?)$", path)
191 return m.groupdict() if m else {}
192
193 @staticmethod
194 def common_folder_path(
195 folder: str,
196 ) -> str:
197 """Returns a fully-qualified folder string."""
198 return "folders/{folder}".format(
199 folder=folder,
200 )
201
202 @staticmethod
203 def parse_common_folder_path(path: str) -> Dict[str, str]:
204 """Parse a folder path into its component segments."""
205 m = re.match(r"^folders/(?P<folder>.+?)$", path)
206 return m.groupdict() if m else {}
207
208 @staticmethod
209 def common_organization_path(
210 organization: str,
211 ) -> str:
212 """Returns a fully-qualified organization string."""
213 return "organizations/{organization}".format(
214 organization=organization,
215 )
216
217 @staticmethod
218 def parse_common_organization_path(path: str) -> Dict[str, str]:
219 """Parse a organization path into its component segments."""
220 m = re.match(r"^organizations/(?P<organization>.+?)$", path)
221 return m.groupdict() if m else {}
222
223 @staticmethod
224 def common_project_path(
225 project: str,
226 ) -> str:
227 """Returns a fully-qualified project string."""
228 return "projects/{project}".format(
229 project=project,
230 )
231
232 @staticmethod
233 def parse_common_project_path(path: str) -> Dict[str, str]:
234 """Parse a project path into its component segments."""
235 m = re.match(r"^projects/(?P<project>.+?)$", path)
236 return m.groupdict() if m else {}
237
238 @staticmethod
239 def common_location_path(
240 project: str,
241 location: str,
242 ) -> str:
243 """Returns a fully-qualified location string."""
244 return "projects/{project}/locations/{location}".format(
245 project=project,
246 location=location,
247 )
248
249 @staticmethod
250 def parse_common_location_path(path: str) -> Dict[str, str]:
251 """Parse a location path into its component segments."""
252 m = re.match(r"^projects/(?P<project>.+?)/locations/(?P<location>.+?)$", path)
253 return m.groupdict() if m else {}
254
255 @classmethod
256 def get_mtls_endpoint_and_cert_source(
257 cls, client_options: Optional[client_options_lib.ClientOptions] = None
258 ):
259 """Return the API endpoint and client cert source for mutual TLS.
260
261 The client cert source is determined in the following order:
262 (1) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is not "true", the
263 client cert source is None.
264 (2) if `client_options.client_cert_source` is provided, use the provided one; if the
265 default client cert source exists, use the default one; otherwise the client cert
266 source is None.
267
268 The API endpoint is determined in the following order:
269 (1) if `client_options.api_endpoint` if provided, use the provided one.
270 (2) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is "always", use the
271 default mTLS endpoint; if the environment variable is "never", use the default API
272 endpoint; otherwise if client cert source exists, use the default mTLS endpoint, otherwise
273 use the default API endpoint.
274
275 More details can be found at https://google.aip.dev/auth/4114.
276
277 Args:
278 client_options (google.api_core.client_options.ClientOptions): Custom options for the
279 client. Only the `api_endpoint` and `client_cert_source` properties may be used
280 in this method.
281
282 Returns:
283 Tuple[str, Callable[[], Tuple[bytes, bytes]]]: returns the API endpoint and the
284 client cert source to use.
285
286 Raises:
287 google.auth.exceptions.MutualTLSChannelError: If any errors happen.
288 """
289 if client_options is None:
290 client_options = client_options_lib.ClientOptions()
291 use_client_cert = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")
292 use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto")
293 if use_client_cert not in ("true", "false"):
294 raise ValueError(
295 "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`"
296 )
297 if use_mtls_endpoint not in ("auto", "never", "always"):
298 raise MutualTLSChannelError(
299 "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`"
300 )
301
302 # Figure out the client cert source to use.
303 client_cert_source = None
304 if use_client_cert == "true":
305 if client_options.client_cert_source:
306 client_cert_source = client_options.client_cert_source
307 elif mtls.has_default_client_cert_source():
308 client_cert_source = mtls.default_client_cert_source()
309
310 # Figure out which api endpoint to use.
311 if client_options.api_endpoint is not None:
312 api_endpoint = client_options.api_endpoint
313 elif use_mtls_endpoint == "always" or (
314 use_mtls_endpoint == "auto" and client_cert_source
315 ):
316 api_endpoint = cls.DEFAULT_MTLS_ENDPOINT
317 else:
318 api_endpoint = cls.DEFAULT_ENDPOINT
319
320 return api_endpoint, client_cert_source
321
322 def __init__(
323 self,
324 *,
325 credentials: Optional[ga_credentials.Credentials] = None,
326 transport: Optional[Union[str, ReportErrorsServiceTransport]] = None,
327 client_options: Optional[Union[client_options_lib.ClientOptions, dict]] = None,
328 client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
329 ) -> None:
330 """Instantiates the report errors service client.
331
332 Args:
333 credentials (Optional[google.auth.credentials.Credentials]): The
334 authorization credentials to attach to requests. These
335 credentials identify the application to the service; if none
336 are specified, the client will attempt to ascertain the
337 credentials from the environment.
338 transport (Union[str, ReportErrorsServiceTransport]): The
339 transport to use. If set to None, a transport is chosen
340 automatically.
341 client_options (Optional[Union[google.api_core.client_options.ClientOptions, dict]]): Custom options for the
342 client. It won't take effect if a ``transport`` instance is provided.
343 (1) The ``api_endpoint`` property can be used to override the
344 default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT
345 environment variable can also be used to override the endpoint:
346 "always" (always use the default mTLS endpoint), "never" (always
347 use the default regular endpoint) and "auto" (auto switch to the
348 default mTLS endpoint if client certificate is present, this is
349 the default value). However, the ``api_endpoint`` property takes
350 precedence if provided.
351 (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable
352 is "true", then the ``client_cert_source`` property can be used
353 to provide client certificate for mutual TLS transport. If
354 not provided, the default SSL client certificate will be used if
355 present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not
356 set, no client certificate will be used.
357 client_info (google.api_core.gapic_v1.client_info.ClientInfo):
358 The client info used to send a user-agent string along with
359 API requests. If ``None``, then default info will be used.
360 Generally, you only need to set this if you're developing
361 your own client library.
362
363 Raises:
364 google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport
365 creation failed for any reason.
366 """
367 if isinstance(client_options, dict):
368 client_options = client_options_lib.from_dict(client_options)
369 if client_options is None:
370 client_options = client_options_lib.ClientOptions()
371 client_options = cast(client_options_lib.ClientOptions, client_options)
372
373 api_endpoint, client_cert_source_func = self.get_mtls_endpoint_and_cert_source(
374 client_options
375 )
376
377 api_key_value = getattr(client_options, "api_key", None)
378 if api_key_value and credentials:
379 raise ValueError(
380 "client_options.api_key and credentials are mutually exclusive"
381 )
382
383 # Save or instantiate the transport.
384 # Ordinarily, we provide the transport, but allowing a custom transport
385 # instance provides an extensibility point for unusual situations.
386 if isinstance(transport, ReportErrorsServiceTransport):
387 # transport is a ReportErrorsServiceTransport instance.
388 if credentials or client_options.credentials_file or api_key_value:
389 raise ValueError(
390 "When providing a transport instance, "
391 "provide its credentials directly."
392 )
393 if client_options.scopes:
394 raise ValueError(
395 "When providing a transport instance, provide its scopes "
396 "directly."
397 )
398 self._transport = transport
399 else:
400 import google.auth._default # type: ignore
401
402 if api_key_value and hasattr(
403 google.auth._default, "get_api_key_credentials"
404 ):
405 credentials = google.auth._default.get_api_key_credentials(
406 api_key_value
407 )
408
409 Transport = type(self).get_transport_class(transport)
410 self._transport = Transport(
411 credentials=credentials,
412 credentials_file=client_options.credentials_file,
413 host=api_endpoint,
414 scopes=client_options.scopes,
415 client_cert_source_for_mtls=client_cert_source_func,
416 quota_project_id=client_options.quota_project_id,
417 client_info=client_info,
418 always_use_jwt_access=True,
419 api_audience=client_options.api_audience,
420 )
421
422 def report_error_event(
423 self,
424 request: Optional[
425 Union[report_errors_service.ReportErrorEventRequest, dict]
426 ] = None,
427 *,
428 project_name: Optional[str] = None,
429 event: Optional[report_errors_service.ReportedErrorEvent] = None,
430 retry: OptionalRetry = gapic_v1.method.DEFAULT,
431 timeout: Union[float, object] = gapic_v1.method.DEFAULT,
432 metadata: Sequence[Tuple[str, str]] = (),
433 ) -> report_errors_service.ReportErrorEventResponse:
434 r"""Report an individual error event and record the event to a log.
435
436 This endpoint accepts **either** an OAuth token, **or** an `API
437 key <https://support.google.com/cloud/answer/6158862>`__ for
438 authentication. To use an API key, append it to the URL as the
439 value of a ``key`` parameter. For example:
440
441 ``POST https://clouderrorreporting.googleapis.com/v1beta1/{projectName}/events:report?key=123ABC456``
442
443 **Note:** `Error Reporting </error-reporting>`__ is a global
444 service built on Cloud Logging and doesn't analyze logs stored
445 in regional log buckets or logs routed to other Google Cloud
446 projects.
447
448 .. code-block:: python
449
450 # This snippet has been automatically generated and should be regarded as a
451 # code template only.
452 # It will require modifications to work:
453 # - It may require correct/in-range values for request initialization.
454 # - It may require specifying regional endpoints when creating the service
455 # client as shown in:
456 # https://googleapis.dev/python/google-api-core/latest/client_options.html
457 from google.cloud import errorreporting_v1beta1
458
459 def sample_report_error_event():
460 # Create a client
461 client = errorreporting_v1beta1.ReportErrorsServiceClient()
462
463 # Initialize request argument(s)
464 event = errorreporting_v1beta1.ReportedErrorEvent()
465 event.message = "message_value"
466
467 request = errorreporting_v1beta1.ReportErrorEventRequest(
468 project_name="project_name_value",
469 event=event,
470 )
471
472 # Make the request
473 response = client.report_error_event(request=request)
474
475 # Handle the response
476 print(response)
477
478 Args:
479 request (Union[google.cloud.errorreporting_v1beta1.types.ReportErrorEventRequest, dict]):
480 The request object. A request for reporting an individual
481 error event.
482 project_name (str):
483 Required. The resource name of the Google Cloud Platform
484 project. Written as ``projects/{projectId}``, where
485 ``{projectId}`` is the `Google Cloud Platform project
486 ID <https://support.google.com/cloud/answer/6158840>`__.
487
488 Example: // ``projects/my-project-123``.
489
490 This corresponds to the ``project_name`` field
491 on the ``request`` instance; if ``request`` is provided, this
492 should not be set.
493 event (google.cloud.errorreporting_v1beta1.types.ReportedErrorEvent):
494 Required. The error event to be
495 reported.
496
497 This corresponds to the ``event`` field
498 on the ``request`` instance; if ``request`` is provided, this
499 should not be set.
500 retry (google.api_core.retry.Retry): Designation of what errors, if any,
501 should be retried.
502 timeout (float): The timeout for this request.
503 metadata (Sequence[Tuple[str, str]]): Strings which should be
504 sent along with the request as metadata.
505
506 Returns:
507 google.cloud.errorreporting_v1beta1.types.ReportErrorEventResponse:
508 Response for reporting an individual
509 error event. Data may be added to this
510 message in the future.
511
512 """
513 # Create or coerce a protobuf request object.
514 # Quick check: If we got a request object, we should *not* have
515 # gotten any keyword arguments that map to the request.
516 has_flattened_params = any([project_name, event])
517 if request is not None and has_flattened_params:
518 raise ValueError(
519 "If the `request` argument is set, then none of "
520 "the individual field arguments should be set."
521 )
522
523 # Minor optimization to avoid making a copy if the user passes
524 # in a report_errors_service.ReportErrorEventRequest.
525 # There's no risk of modifying the input as we've already verified
526 # there are no flattened fields.
527 if not isinstance(request, report_errors_service.ReportErrorEventRequest):
528 request = report_errors_service.ReportErrorEventRequest(request)
529 # If we have keyword arguments corresponding to fields on the
530 # request, apply these.
531 if project_name is not None:
532 request.project_name = project_name
533 if event is not None:
534 request.event = event
535
536 # Wrap the RPC method; this adds retry and timeout information,
537 # and friendly error handling.
538 rpc = self._transport._wrapped_methods[self._transport.report_error_event]
539
540 # Certain fields should be provided within the metadata header;
541 # add these here.
542 metadata = tuple(metadata) + (
543 gapic_v1.routing_header.to_grpc_metadata(
544 (("project_name", request.project_name),)
545 ),
546 )
547
548 # Send the request.
549 response = rpc(
550 request,
551 retry=retry,
552 timeout=timeout,
553 metadata=metadata,
554 )
555
556 # Done; return the response.
557 return response
558
559 def __enter__(self) -> "ReportErrorsServiceClient":
560 return self
561
562 def __exit__(self, type, value, traceback):
563 """Releases underlying transport's resources.
564
565 .. warning::
566 ONLY use as a context manager if the transport is NOT shared
567 with other clients! Exiting the with block will CLOSE the transport
568 and may cause errors in other clients!
569 """
570 self.transport.close()
571
572
573DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(
574 gapic_version=package_version.__version__
575)
576
577
578__all__ = ("ReportErrorsServiceClient",)