Skip to content

Commit

Permalink
Merge pull request #583 from googleads/load-oauth-stream
Browse files Browse the repository at this point in the history
Expose loading the OAuth2 secrets from a stream
  • Loading branch information
Raibaz authored Nov 14, 2024
2 parents cd0aabf + b85f199 commit d935f0c
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 70 deletions.
49 changes: 33 additions & 16 deletions Google.Ads.Gax/src/Config/AdsConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,37 @@ public override void ReadSettings(Dictionary<string, string> settings)
ReadProxySettings(settings);
}

/// <summary>
/// Loads the OAuth2 secrets from a stream reader. Note that the stream reader will not be
/// disposed of.
/// </summary>
/// <param name="reader"> A stream reader.</param>
public void LoadOAuth2SecretsFromStream(StreamReader reader)
{
try
{
string contents = reader.ReadToEnd();
Dictionary<string, string> config =
JsonConvert.DeserializeObject<Dictionary<string, string>>(contents);

ReadSetting(config, oAuth2ServiceAccountEmail);
if (string.IsNullOrEmpty(this.OAuth2ServiceAccountEmail))
{
throw new ApplicationException(ErrorMessages.ClientEmailIsMissingInJson);
}

ReadSetting(config, oAuth2PrivateKey);
if (string.IsNullOrEmpty(this.OAuth2PrivateKey))
{
throw new ApplicationException(ErrorMessages.PrivateKeyIsMissingInJson);
}
}
catch (Exception e)
{
throw new ArgumentException(ErrorMessages.FailedToLoadJsonSecrets, e);
}
}

/// <summary>
/// Reads the proxy settings.
/// </summary>
Expand Down Expand Up @@ -596,26 +627,12 @@ private void LoadOAuth2SecretsFromFile()
{
using (StreamReader reader = new StreamReader(OAuth2SecretsJsonPath))
{
string contents = reader.ReadToEnd();
Dictionary<string, string> config =
JsonConvert.DeserializeObject<Dictionary<string, string>>(contents);

ReadSetting(config, oAuth2ServiceAccountEmail);
if (string.IsNullOrEmpty(this.OAuth2ServiceAccountEmail))
{
throw new ApplicationException(ErrorMessages.ClientEmailIsMissingInJsonFile);
}

ReadSetting(config, oAuth2PrivateKey);
if (string.IsNullOrEmpty(this.OAuth2PrivateKey))
{
throw new ApplicationException(ErrorMessages.PrivateKeyIsMissingInJsonFile);
}
LoadOAuth2SecretsFromStream(reader);
}
}
catch (Exception e)
{
throw new ArgumentException(ErrorMessages.FailedToLoadJsonSecretsFile, e);
throw new ArgumentException(ErrorMessages.FailedToLoadJsonSecrets, e);
}
}
}
Expand Down
42 changes: 21 additions & 21 deletions Google.Ads.Gax/src/ErrorMessages.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

66 changes: 33 additions & 33 deletions Google.Ads.Gax/src/ErrorMessages.resx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
Expand All @@ -26,36 +26,36 @@
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
Expand Down Expand Up @@ -117,24 +117,24 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="ClientEmailIsMissingInJsonFile" xml:space="preserve">
<value>Required key 'client_email' is missing in JSON secrets file.</value>
<data name="ClientEmailIsMissingInJson" xml:space="preserve">
<value>Required key 'client_email' is missing in JSON secrets.</value>
<comment>Used by AdsConfig class when trying to load various settings from the JSON secrets file when using OAuth2 service account flow. Thrown if client_email key is missing in the JSON file.</comment>
</data>
<data name="ConfigSettingParseError" xml:space="preserve">
<value>Failed to parse value for setting name '{0}'. Value provided: '{1}'.</value>
<comment>Used when a ConfigSetting cannot parse a value.</comment>
</data>
<data name="FailedToLoadJsonSecretsFile" xml:space="preserve">
<value>Failed to load JSON secrets file for service account configuration. See inner exception for details.</value>
<data name="FailedToLoadJsonSecrets" xml:space="preserve">
<value>Failed to load JSON secrets for service account configuration. See inner exception for details.</value>
<comment>Used by AdsConfig class when trying to load various settings from the JSON secrets file when using OAuth2 service account flow.</comment>
</data>
<data name="GrpcNetClientNotSupported" xml:space="preserve">
<value>Grpc.Net.Client is not supported on this platform. See https://docs.microsoft.com/en-us/aspnet/core/grpc/supported-platforms?view=aspnetcore-6.0 for requirements.</value>
<comment>Used by AdsChannel.cs when trying to choose Grpc.Net.Client as the transport.</comment>
</data>
<data name="PrivateKeyIsMissingInJsonFile" xml:space="preserve">
<value>Required key 'private_key' is missing in JSON secrets file.</value>
<data name="PrivateKeyIsMissingInJson" xml:space="preserve">
<value>Required key 'private_key' is missing in JSON secrets.</value>
<comment>Used by AdsConfig class when trying to load various settings from the JSON secrets file when using OAuth2 service account flow. Thrown if private_key key is missing in the JSON file.</comment>
</data>
</root>
73 changes: 73 additions & 0 deletions Google.Ads.Gax/tests/Config/AdsConfigTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright 2024 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.

using Google.Ads.Gax.Config;
using NUnit.Framework;
using System;
using System.IO;
using System.Text;

namespace Google.Ads.Gax.Tests.Config
{
/// <summary>
/// Tests for <see cref="AdsConfig"/> class.
/// </summary>
[TestFixture]
[Category("Smoke")]
internal class AdsConfigTests
{

/// <summary>
/// Tests if a stream containing OAuth2 secrets is loaded correctly.
/// </summary>
[Test]
public void TestLoadOauth2SecretsFromStream()
{
string testCredentials = "{\"client_email\": \"[email protected]\", \"private_key\": \"test_key\"}";

AdsConfig config = new AdsConfig();
config.LoadOAuth2SecretsFromStream(new StreamReader(new MemoryStream(Encoding.UTF8.GetBytes(testCredentials))));
Assert.AreEqual("[email protected]", config.OAuth2ServiceAccountEmail);
}

/// <summary>
/// Tests if loading a stream without the private key throws an exception.
/// </summary>
[Test]
public void TestLoadOauth2SecretsFromStreamWithoutPrivateKeyThrowsException()
{
string testCredentials = "{\"client_email\": \"[email protected]\"}";

AdsConfig config = new AdsConfig();
Assert.Throws<ArgumentException>(() => config.LoadOAuth2SecretsFromStream(
new StreamReader(new MemoryStream(Encoding.UTF8.GetBytes(testCredentials)))
));
}

/// <summary>
/// Tests if loading a stream without the client email throws an exception.
/// </summary>
[Test]
public void TestLoadOauth2SecretsFromStreamWithoutClientEmailThrowsException()
{
string testCredentials = "{\"private_key\": \"test_key\"}";

AdsConfig config = new AdsConfig();
Assert.Throws<ArgumentException>(() => config.LoadOAuth2SecretsFromStream(
new StreamReader(new MemoryStream(Encoding.UTF8.GetBytes(testCredentials)))
));
}

}
}

0 comments on commit d935f0c

Please sign in to comment.