From 8fc5a1255bc6a3e44df1e953e08caf7799f6ddcc Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Wed, 8 May 2019 16:47:36 -0700 Subject: [PATCH 1/9] Add ClientOptions class to api_core. --- api_core/google/api_core/client_options.py | 44 ++++++++++++++++++++++ api_core/tests/unit/test_client_options.py | 37 ++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 api_core/google/api_core/client_options.py create mode 100644 api_core/tests/unit/test_client_options.py diff --git a/api_core/google/api_core/client_options.py b/api_core/google/api_core/client_options.py new file mode 100644 index 000000000000..1fcd2a2e9e58 --- /dev/null +++ b/api_core/google/api_core/client_options.py @@ -0,0 +1,44 @@ +# Copyright 2019 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. + +"""Client options class. + +Client options provide an consistent interface for user options to be defined +across clients. +""" + +class ClientOptions(object): + """Client Options used to set options on clients. + + Args: + options (dict): A dict of the options listed below. + api_endpoint (str): The desired API endpoint, e.g., compute.googleapis.com + """ + + api_endpoint = None + + def __init__(self, options=None, **kw_args): + if options is not None: + if kw_args.items(): + raise Exception("ClientOptions expects options in a dictionary or in kw_args, not both") + client_options = options + else: + client_options = kw_args + + for key, value in client_options.items(): + if not hasattr(self, key): + raise ValueError( + "ClientOptions does not accept an argument named '" + key + "'" + ) + setattr(self, key, value) diff --git a/api_core/tests/unit/test_client_options.py b/api_core/tests/unit/test_client_options.py new file mode 100644 index 000000000000..a853fd325ab4 --- /dev/null +++ b/api_core/tests/unit/test_client_options.py @@ -0,0 +1,37 @@ +# Copyright 2017 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. + +import pytest + +from google.api_core import client_options + + +def test_constructor_dict(): + options = client_options.ClientOptions( + options={"api_endpoint": "foo.googleapis.com"} + ) + + assert options.api_endpoint == "foo.googleapis.com" + +def test_constructor_kwargs(): + options = client_options.ClientOptions( + api_endpoint="foo.googleapis.com" + ) + + assert options.api_endpoint == "foo.googleapis.com" + +def test_constructor_exception(): + with pytest.raises(Exception): + options = client_options.ClientOptions( + api_endpoint="a.googleapis.com", options={"b.googleapis.com"}) \ No newline at end of file From e56133a7b6c0958ef1755817be866cabdf8638e0 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Fri, 7 Jun 2019 14:31:58 -0700 Subject: [PATCH 2/9] Modify IoT to respect api_endpoint in client options. --- .../cloud/iot_v1/gapic/device_manager_client.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/iot/google/cloud/iot_v1/gapic/device_manager_client.py b/iot/google/cloud/iot_v1/gapic/device_manager_client.py index e56788485761..63bc14e481c0 100644 --- a/iot/google/cloud/iot_v1/gapic/device_manager_client.py +++ b/iot/google/cloud/iot_v1/gapic/device_manager_client.py @@ -22,6 +22,7 @@ from google.oauth2 import service_account import google.api_core.gapic_v1.client_info +import google.api_core.client_options import google.api_core.gapic_v1.config import google.api_core.gapic_v1.method import google.api_core.gapic_v1.routing_header @@ -112,6 +113,7 @@ def __init__( credentials=None, client_config=None, client_info=None, + client_options=None, ): """Constructor. @@ -142,6 +144,9 @@ def __init__( API requests. If ``None``, then default info will be used. Generally, you only need to set this if you're developing your own client library. + client_options (google.api_core.gapic_v1.client_options.ClientOptions): + Client options used to set user options on the client. API Endpoint + should be set through client_options. """ # Raise deprecation warnings for things we want to go away. if client_config is not None: @@ -160,6 +165,11 @@ def __init__( stacklevel=2, ) + api_endpoint = self.SERVICE_ADDRESS + if client_options: + if client_options.api_endpoint: + api_endpoint = client_options.api_endpoint + # Instantiate the transport. # The transport is responsible for handling serialization and # deserialization and actually sending data to the service. @@ -168,6 +178,7 @@ def __init__( self.transport = transport( credentials=credentials, default_class=device_manager_grpc_transport.DeviceManagerGrpcTransport, + address=api_endpoint, ) else: if credentials: @@ -178,7 +189,7 @@ def __init__( self.transport = transport else: self.transport = device_manager_grpc_transport.DeviceManagerGrpcTransport( - address=self.SERVICE_ADDRESS, channel=channel, credentials=credentials + address=api_endpoint, channel=channel, credentials=credentials ) if client_info is None: From 6e7464788893db6bab9383ef559f4e54fad4a844 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Fri, 7 Jun 2019 14:37:01 -0700 Subject: [PATCH 3/9] Blacken. --- api_core/google/api_core/client_options.py | 5 ++++- api_core/tests/unit/test_client_options.py | 9 +++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/api_core/google/api_core/client_options.py b/api_core/google/api_core/client_options.py index 1fcd2a2e9e58..686f3988939b 100644 --- a/api_core/google/api_core/client_options.py +++ b/api_core/google/api_core/client_options.py @@ -18,6 +18,7 @@ across clients. """ + class ClientOptions(object): """Client Options used to set options on clients. @@ -31,7 +32,9 @@ class ClientOptions(object): def __init__(self, options=None, **kw_args): if options is not None: if kw_args.items(): - raise Exception("ClientOptions expects options in a dictionary or in kw_args, not both") + raise Exception( + "ClientOptions expects options in a dictionary or in kw_args, not both" + ) client_options = options else: client_options = kw_args diff --git a/api_core/tests/unit/test_client_options.py b/api_core/tests/unit/test_client_options.py index a853fd325ab4..ddd6c219afd9 100644 --- a/api_core/tests/unit/test_client_options.py +++ b/api_core/tests/unit/test_client_options.py @@ -24,14 +24,15 @@ def test_constructor_dict(): assert options.api_endpoint == "foo.googleapis.com" + def test_constructor_kwargs(): - options = client_options.ClientOptions( - api_endpoint="foo.googleapis.com" - ) + options = client_options.ClientOptions(api_endpoint="foo.googleapis.com") assert options.api_endpoint == "foo.googleapis.com" + def test_constructor_exception(): with pytest.raises(Exception): options = client_options.ClientOptions( - api_endpoint="a.googleapis.com", options={"b.googleapis.com"}) \ No newline at end of file + api_endpoint="a.googleapis.com", options={"b.googleapis.com"} + ) From 17caa5485a203c95ea1e9e22e5fd4f54f7e6dcfd Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Fri, 7 Jun 2019 14:38:34 -0700 Subject: [PATCH 4/9] Fix incorrect type in docstring. --- iot/google/cloud/iot_v1/gapic/device_manager_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iot/google/cloud/iot_v1/gapic/device_manager_client.py b/iot/google/cloud/iot_v1/gapic/device_manager_client.py index 63bc14e481c0..da58ec0c5dcf 100644 --- a/iot/google/cloud/iot_v1/gapic/device_manager_client.py +++ b/iot/google/cloud/iot_v1/gapic/device_manager_client.py @@ -144,7 +144,7 @@ def __init__( API requests. If ``None``, then default info will be used. Generally, you only need to set this if you're developing your own client library. - client_options (google.api_core.gapic_v1.client_options.ClientOptions): + client_options (google.api_core.client_options.ClientOptions): Client options used to set user options on the client. API Endpoint should be set through client_options. """ From be73bba2883ab7cc78780eea1b9e20a93626e69e Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Fri, 7 Jun 2019 15:06:27 -0700 Subject: [PATCH 5/9] Fix lint, raise coverage to 100. --- api_core/google/api_core/client_options.py | 2 +- api_core/tests/unit/test_client_options.py | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/api_core/google/api_core/client_options.py b/api_core/google/api_core/client_options.py index 686f3988939b..ad6fa57bd1ff 100644 --- a/api_core/google/api_core/client_options.py +++ b/api_core/google/api_core/client_options.py @@ -20,7 +20,7 @@ class ClientOptions(object): - """Client Options used to set options on clients. + """Client Options used to set options on clients. Args: options (dict): A dict of the options listed below. diff --git a/api_core/tests/unit/test_client_options.py b/api_core/tests/unit/test_client_options.py index ddd6c219afd9..a4fcbc377015 100644 --- a/api_core/tests/unit/test_client_options.py +++ b/api_core/tests/unit/test_client_options.py @@ -33,6 +33,11 @@ def test_constructor_kwargs(): def test_constructor_exception(): with pytest.raises(Exception): - options = client_options.ClientOptions( + client_options.ClientOptions( api_endpoint="a.googleapis.com", options={"b.googleapis.com"} ) + + +def test_constructor_bad_option(): + with pytest.raises(ValueError): + client_options.ClientOptions(options={"bad_option": "foo"}) From fc8e1a5bb2c8d263ed60239a90fe63ddc7949fb5 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Mon, 10 Jun 2019 16:55:48 -0700 Subject: [PATCH 6/9] Use named arguments, add method to construct client_options from dict. --- api_core/google/api_core/client_options.py | 37 ++++++++++--------- api_core/tests/unit/test_client_options.py | 23 ++++-------- .../iot_v1/gapic/device_manager_client.py | 4 +- 3 files changed, 30 insertions(+), 34 deletions(-) diff --git a/api_core/google/api_core/client_options.py b/api_core/google/api_core/client_options.py index ad6fa57bd1ff..580aba9f50c3 100644 --- a/api_core/google/api_core/client_options.py +++ b/api_core/google/api_core/client_options.py @@ -14,7 +14,7 @@ """Client options class. -Client options provide an consistent interface for user options to be defined +Client options provide a consistent interface for user options to be defined across clients. """ @@ -23,25 +23,26 @@ class ClientOptions(object): """Client Options used to set options on clients. Args: - options (dict): A dict of the options listed below. api_endpoint (str): The desired API endpoint, e.g., compute.googleapis.com """ - api_endpoint = None + def __init__(self, api_endpoint=None): + self.api_endpoint = api_endpoint - def __init__(self, options=None, **kw_args): - if options is not None: - if kw_args.items(): - raise Exception( - "ClientOptions expects options in a dictionary or in kw_args, not both" - ) - client_options = options + +def from_dict(options): + """Construct a client options object from a dictionary. + + Args: + options (dict): A dictionary with client options. + """ + + client_options = ClientOptions() + + for key, value in options.items(): + if hasattr(client_options, key): + setattr(client_options, key, value) else: - client_options = kw_args - - for key, value in client_options.items(): - if not hasattr(self, key): - raise ValueError( - "ClientOptions does not accept an argument named '" + key + "'" - ) - setattr(self, key, value) + raise ValueError("ClientOptions does not accept an option '" + key + "'") + + return client_options diff --git a/api_core/tests/unit/test_client_options.py b/api_core/tests/unit/test_client_options.py index a4fcbc377015..703eab5f6ac0 100644 --- a/api_core/tests/unit/test_client_options.py +++ b/api_core/tests/unit/test_client_options.py @@ -17,27 +17,20 @@ from google.api_core import client_options -def test_constructor_dict(): - options = client_options.ClientOptions( - options={"api_endpoint": "foo.googleapis.com"} - ) +def test_constructor(): + options = client_options.ClientOptions(api_endpoint="foo.googleapis.com") assert options.api_endpoint == "foo.googleapis.com" -def test_constructor_kwargs(): - options = client_options.ClientOptions(api_endpoint="foo.googleapis.com") +def test_from_dict(): + options = client_options.from_dict({"api_endpoint": "foo.googleapis.com"}) assert options.api_endpoint == "foo.googleapis.com" -def test_constructor_exception(): - with pytest.raises(Exception): - client_options.ClientOptions( - api_endpoint="a.googleapis.com", options={"b.googleapis.com"} - ) - - -def test_constructor_bad_option(): +def test_from_dict_bad_argument(): with pytest.raises(ValueError): - client_options.ClientOptions(options={"bad_option": "foo"}) + client_options.from_dict( + {"api_endpoint": "foo.googleapis.com", "bad_arg": "1234"} + ) diff --git a/iot/google/cloud/iot_v1/gapic/device_manager_client.py b/iot/google/cloud/iot_v1/gapic/device_manager_client.py index da58ec0c5dcf..449f5f78b4fc 100644 --- a/iot/google/cloud/iot_v1/gapic/device_manager_client.py +++ b/iot/google/cloud/iot_v1/gapic/device_manager_client.py @@ -144,7 +144,7 @@ def __init__( API requests. If ``None``, then default info will be used. Generally, you only need to set this if you're developing your own client library. - client_options (google.api_core.client_options.ClientOptions): + client_options (Union[dict, google.api_core.client_options.ClientOptions): Client options used to set user options on the client. API Endpoint should be set through client_options. """ @@ -167,6 +167,8 @@ def __init__( api_endpoint = self.SERVICE_ADDRESS if client_options: + if type(client_options) == dict: + client_options = google.api-core.client_options.from_dict(client_options) if client_options.api_endpoint: api_endpoint = client_options.api_endpoint From 2f448b6a54074c38def47fa4af63b0e6231e12fa Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Mon, 10 Jun 2019 16:59:32 -0700 Subject: [PATCH 7/9] Fix typo in device_manager_client. --- iot/google/cloud/iot_v1/gapic/device_manager_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iot/google/cloud/iot_v1/gapic/device_manager_client.py b/iot/google/cloud/iot_v1/gapic/device_manager_client.py index 449f5f78b4fc..0d6909e83784 100644 --- a/iot/google/cloud/iot_v1/gapic/device_manager_client.py +++ b/iot/google/cloud/iot_v1/gapic/device_manager_client.py @@ -168,7 +168,7 @@ def __init__( api_endpoint = self.SERVICE_ADDRESS if client_options: if type(client_options) == dict: - client_options = google.api-core.client_options.from_dict(client_options) + client_options = google.api_core.client_options.from_dict(client_options) if client_options.api_endpoint: api_endpoint = client_options.api_endpoint From 4447da0914352387b4b9141385bcda6ec9b91b7a Mon Sep 17 00:00:00 2001 From: Bu Sun Kim <8822365+busunkim96@users.noreply.github.com> Date: Fri, 21 Jun 2019 16:12:29 -0700 Subject: [PATCH 8/9] Update test_client_options.py --- api_core/tests/unit/test_client_options.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api_core/tests/unit/test_client_options.py b/api_core/tests/unit/test_client_options.py index 703eab5f6ac0..14cae9f24bfb 100644 --- a/api_core/tests/unit/test_client_options.py +++ b/api_core/tests/unit/test_client_options.py @@ -1,4 +1,4 @@ -# Copyright 2017 Google LLC +# Copyright 2019 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. From 8c6af56e107d1f802e1dab05fb8c3002f1b5123a Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Fri, 21 Jun 2019 16:13:55 -0700 Subject: [PATCH 9/9] Revert iot changes. --- iot/.coveragerc | 1 + iot/.flake8 | 1 + .../cloud/iot_v1/gapic/device_manager_client.py | 15 +-------------- .../transports/device_manager_grpc_transport.py | 8 ++++++-- .../cloud/iot_v1/proto/device_manager_pb2.py | 1 + iot/google/cloud/iot_v1/proto/resources_pb2.py | 1 + iot/noxfile.py | 6 ++++-- iot/setup.cfg | 1 + iot/synth.metadata | 10 +++++----- 9 files changed, 21 insertions(+), 23 deletions(-) diff --git a/iot/.coveragerc b/iot/.coveragerc index 6b9ab9da4a1b..b178b094aa1d 100644 --- a/iot/.coveragerc +++ b/iot/.coveragerc @@ -1,3 +1,4 @@ +# Generated by synthtool. DO NOT EDIT! [run] branch = True diff --git a/iot/.flake8 b/iot/.flake8 index 61766fa84d02..0268ecc9c55c 100644 --- a/iot/.flake8 +++ b/iot/.flake8 @@ -1,3 +1,4 @@ +# Generated by synthtool. DO NOT EDIT! [flake8] ignore = E203, E266, E501, W503 exclude = diff --git a/iot/google/cloud/iot_v1/gapic/device_manager_client.py b/iot/google/cloud/iot_v1/gapic/device_manager_client.py index 0d6909e83784..e56788485761 100644 --- a/iot/google/cloud/iot_v1/gapic/device_manager_client.py +++ b/iot/google/cloud/iot_v1/gapic/device_manager_client.py @@ -22,7 +22,6 @@ from google.oauth2 import service_account import google.api_core.gapic_v1.client_info -import google.api_core.client_options import google.api_core.gapic_v1.config import google.api_core.gapic_v1.method import google.api_core.gapic_v1.routing_header @@ -113,7 +112,6 @@ def __init__( credentials=None, client_config=None, client_info=None, - client_options=None, ): """Constructor. @@ -144,9 +142,6 @@ def __init__( API requests. If ``None``, then default info will be used. Generally, you only need to set this if you're developing your own client library. - client_options (Union[dict, google.api_core.client_options.ClientOptions): - Client options used to set user options on the client. API Endpoint - should be set through client_options. """ # Raise deprecation warnings for things we want to go away. if client_config is not None: @@ -165,13 +160,6 @@ def __init__( stacklevel=2, ) - api_endpoint = self.SERVICE_ADDRESS - if client_options: - if type(client_options) == dict: - client_options = google.api_core.client_options.from_dict(client_options) - if client_options.api_endpoint: - api_endpoint = client_options.api_endpoint - # Instantiate the transport. # The transport is responsible for handling serialization and # deserialization and actually sending data to the service. @@ -180,7 +168,6 @@ def __init__( self.transport = transport( credentials=credentials, default_class=device_manager_grpc_transport.DeviceManagerGrpcTransport, - address=api_endpoint, ) else: if credentials: @@ -191,7 +178,7 @@ def __init__( self.transport = transport else: self.transport = device_manager_grpc_transport.DeviceManagerGrpcTransport( - address=api_endpoint, channel=channel, credentials=credentials + address=self.SERVICE_ADDRESS, channel=channel, credentials=credentials ) if client_info is None: diff --git a/iot/google/cloud/iot_v1/gapic/transports/device_manager_grpc_transport.py b/iot/google/cloud/iot_v1/gapic/transports/device_manager_grpc_transport.py index 97a85173da68..9af59a3076b6 100644 --- a/iot/google/cloud/iot_v1/gapic/transports/device_manager_grpc_transport.py +++ b/iot/google/cloud/iot_v1/gapic/transports/device_manager_grpc_transport.py @@ -72,7 +72,9 @@ def __init__( } @classmethod - def create_channel(cls, address="cloudiot.googleapis.com:443", credentials=None): + def create_channel( + cls, address="cloudiot.googleapis.com:443", credentials=None, **kwargs + ): """Create and return a gRPC channel object. Args: @@ -82,12 +84,14 @@ def create_channel(cls, address="cloudiot.googleapis.com:443", credentials=None) credentials identify this application to the service. If none are specified, the client will attempt to ascertain the credentials from the environment. + kwargs (dict): Keyword arguments, which are passed to the + channel creation. Returns: grpc.Channel: A gRPC channel object. """ return google.api_core.grpc_helpers.create_channel( - address, credentials=credentials, scopes=cls._OAUTH_SCOPES + address, credentials=credentials, scopes=cls._OAUTH_SCOPES, **kwargs ) @property diff --git a/iot/google/cloud/iot_v1/proto/device_manager_pb2.py b/iot/google/cloud/iot_v1/proto/device_manager_pb2.py index f5675a23a145..f4120bfd6d52 100644 --- a/iot/google/cloud/iot_v1/proto/device_manager_pb2.py +++ b/iot/google/cloud/iot_v1/proto/device_manager_pb2.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: google/cloud/iot_v1/proto/device_manager.proto diff --git a/iot/google/cloud/iot_v1/proto/resources_pb2.py b/iot/google/cloud/iot_v1/proto/resources_pb2.py index f99590199286..86af058d4b59 100644 --- a/iot/google/cloud/iot_v1/proto/resources_pb2.py +++ b/iot/google/cloud/iot_v1/proto/resources_pb2.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: google/cloud/iot_v1/proto/resources.proto diff --git a/iot/noxfile.py b/iot/noxfile.py index d9aa5166405b..7e3123d42473 100644 --- a/iot/noxfile.py +++ b/iot/noxfile.py @@ -14,6 +14,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# Generated by synthtool. DO NOT EDIT! + from __future__ import absolute_import import os import shutil @@ -46,7 +48,7 @@ def blacken(session): """Run black. Format code to uniform standard. - + This currently uses Python 3.6 due to the automated Kokoro run of synthtool. That run uses an image that doesn't have 3.6 installed. Before updating this check the state of the `gcp_ubuntu_config` we use for that Kokoro run. @@ -78,7 +80,7 @@ def default(session): "--cov-append", "--cov-config=.coveragerc", "--cov-report=", - "--cov-fail-under=80", + "--cov-fail-under=0", os.path.join("tests", "unit"), *session.posargs, ) diff --git a/iot/setup.cfg b/iot/setup.cfg index 2a9acf13daa9..3bd555500e37 100644 --- a/iot/setup.cfg +++ b/iot/setup.cfg @@ -1,2 +1,3 @@ +# Generated by synthtool. DO NOT EDIT! [bdist_wheel] universal = 1 diff --git a/iot/synth.metadata b/iot/synth.metadata index a0bb82eec072..d34582a9972b 100644 --- a/iot/synth.metadata +++ b/iot/synth.metadata @@ -1,19 +1,19 @@ { - "updateTime": "2019-05-25T12:22:48.919409Z", + "updateTime": "2019-06-18T12:19:55.701594Z", "sources": [ { "generator": { "name": "artman", - "version": "0.21.0", - "dockerImage": "googleapis/artman@sha256:28d4271586772b275cd3bc95cb46bd227a24d3c9048de45dccdb7f3afb0bfba9" + "version": "0.27.0", + "dockerImage": "googleapis/artman@sha256:b036a7f4278d9deb5796f065e5c7f608d47d75369985ca7ab5039998120e972d" } }, { "git": { "name": "googleapis", "remote": "https://github.com/googleapis/googleapis.git", - "sha": "7ca19138ccebe219a67be2245200e821b3e32123", - "internalRef": "249916728" + "sha": "384aa843867c4d17756d14a01f047b6368494d32", + "internalRef": "253675319" } }, {