Skip to content
This repository was archived by the owner on Dec 3, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line numberDiff line numberDiff line change
Expand Up@@ -176,6 +176,7 @@ ApiClientHeaderProvider.Builder getInternalHeaderProviderBuilder(
builder.setClientLibToken(
ServiceOptions.getGoogApiClientLibName(),
GaxProperties.getLibraryVersion(serviceOptions.getClass()));
builder.setQuotaProjectIdToken(serviceOptions.getQuotaProjectId());
return builder;
}

Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -43,6 +43,7 @@
import com.google.api.gax.rpc.NoHeaderProvider;
import com.google.auth.Credentials;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.auth.oauth2.QuotaProjectIdProvider;
import com.google.auth.oauth2.ServiceAccountCredentials;
import com.google.cloud.spi.ServiceRpcFactory;
import com.google.common.base.Preconditions;
Expand DownExpand Up@@ -102,6 +103,7 @@ public abstract class ServiceOptions<
protected Credentials credentials;
private final TransportOptions transportOptions;
private final HeaderProvider headerProvider;
private final String quotaProjectId;

private transient ServiceRpcFactory<OptionsT> serviceRpcFactory;
private transient ServiceFactory<ServiceT, OptionsT> serviceFactory;
Expand DownExpand Up@@ -132,6 +134,7 @@ public abstract static class Builder<
private TransportOptions transportOptions;
private HeaderProvider headerProvider;
private String clientLibToken = ServiceOptions.getGoogApiClientLibName();
private String quotaProjectId;

@InternalApi("This class should only be extended within google-cloud-java")
protected Builder() {}
Expand All@@ -147,6 +150,7 @@ protected Builder(ServiceOptions<ServiceT, OptionsT> options) {
clock = options.clock;
transportOptions = options.transportOptions;
clientLibToken = options.clientLibToken;
quotaProjectId = options.quotaProjectId;
}

protected abstract ServiceOptions<ServiceT, OptionsT> build();
Expand DownExpand Up@@ -212,6 +216,10 @@ public B setCredentials(Credentials credentials) {
if (this.projectId == null && credentials instanceof ServiceAccountCredentials) {
this.projectId = ((ServiceAccountCredentials) credentials).getProjectId();
}

Comment thread
chingor13 marked this conversation as resolved.
if (this.quotaProjectId == null && credentials instanceof QuotaProjectIdProvider) {
this.quotaProjectId = ((ServiceAccountCredentials) credentials).getQuotaProjectId();
}
return self();
}

Expand DownExpand Up@@ -269,6 +277,17 @@ public B setClientLibToken(String clientLibToken) {
return self();
}

/**
* Sets the quotaProjectId that specifies the project used for quota and billing purposes.
*
* @see <a href="https://cloud.google.com/apis/docs/system-parameters">See system parameter
* $userProject</a>
*/
public B setQuotaProjectId(String quotaProjectId) {
this.quotaProjectId = quotaProjectId;
return self();
}

protected Set<String> getAllowedClientLibTokens() {
return allowedClientLibTokens;
}
Expand DownExpand Up@@ -305,6 +324,10 @@ protected ServiceOptions(
firstNonNull(builder.transportOptions, serviceDefaults.getDefaultTransportOptions());
headerProvider = firstNonNull(builder.headerProvider, new NoHeaderProvider());
clientLibToken = builder.clientLibToken;
quotaProjectId =
builder.quotaProjectId != null
? builder.quotaProjectId
: getValueFromCredentialsFile(System.getenv(CREDENTIAL_ENV_NAME), "quota_project_id");
}

/**
Expand DownExpand Up@@ -488,24 +511,24 @@ static boolean headerContainsMetadataFlavor(HttpResponse response) {
}

protected static String getServiceAccountProjectId() {
return getServiceAccountProjectId(System.getenv(CREDENTIAL_ENV_NAME));
return getValueFromCredentialsFile(System.getenv(CREDENTIAL_ENV_NAME), "project_id");
}

@InternalApi("Visible for testing")
static String getServiceAccountProjectId(String credentialsPath) {
String project = null;
static String getValueFromCredentialsFile(String credentialsPath, String key) {
String value = null;
if (credentialsPath != null) {
try (InputStream credentialsStream = new FileInputStream(credentialsPath)) {
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
JsonObjectParser parser = new JsonObjectParser(jsonFactory);
GenericJson fileContents =
parser.parseAndClose(credentialsStream, Charsets.UTF_8, GenericJson.class);
project = (String) fileContents.get("project_id");
value = (String) fileContents.get(key);
} catch (IOException e) {
// ignore
}
}
return project;
return value;
}

/**
Expand DownExpand Up@@ -664,7 +687,8 @@ protected int baseHashCode() {
retrySettings,
serviceFactoryClassName,
serviceRpcFactoryClassName,
clock);
clock,
quotaProjectId);
}

protected boolean baseEquals(ServiceOptions<?, ?> other) {
Expand All@@ -674,7 +698,8 @@ protected boolean baseEquals(ServiceOptions<?, ?> other) {
&& Objects.equals(retrySettings, other.retrySettings)
&& Objects.equals(serviceFactoryClassName, other.serviceFactoryClassName)
&& Objects.equals(serviceRpcFactoryClassName, other.serviceRpcFactoryClassName)
&& Objects.equals(clock, clock);
&& Objects.equals(clock, clock)
&& Objects.equals(quotaProjectId, other.quotaProjectId);
}

private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException {
Expand DownExpand Up@@ -734,4 +759,9 @@ public static <T> T getFromServiceLoader(Class<? extends T> clazz, T defaultInst
public String getClientLibToken() {
return clientLibToken;
}

/** Returns the quotaProjectId that specifies the project used for quota and billing purposes. */
public String getQuotaProjectId() {
return quotaProjectId;
}
}
Original file line numberDiff line numberDiff line change
Expand Up@@ -53,6 +53,10 @@
import org.junit.rules.ExpectedException;

public class ServiceOptionsTest {
private static GoogleCredentials credentials;
private static GoogleCredentials credentialsWithProjectId;
private static GoogleCredentials credentialsWithQuotaProject;

private static final String JSON_KEY =
"{\n"
+ " \"private_key_id\": \"somekeyid\",\n"
Expand DownExpand Up@@ -80,16 +84,6 @@ public class ServiceOptionsTest {
+ " \"client_id\": \"someclientid.apps.googleusercontent.com\",\n"
+ " \"type\": \"service_account\"\n"
+ "}";
private static GoogleCredentials credentials;

static {
try {
InputStream keyStream = new ByteArrayInputStream(JSON_KEY.getBytes());
credentials = GoogleCredentials.fromStream(keyStream);
} catch (IOException e) {
fail("Couldn't create fake JSON credentials.");
}
}

private static final String JSON_KEY_PROJECT_ID =
"{\n"
Expand DownExpand Up@@ -119,15 +113,51 @@ public class ServiceOptionsTest {
+ " \"client_id\": \"someclientid.apps.googleusercontent.com\",\n"
+ " \"type\": \"service_account\"\n"
+ "}";
private static GoogleCredentials credentialsWithProjectId;

private static final String JSON_KEY_QUOTA_PROJECT_ID =
"{\n"
+ " \"private_key_id\": \"somekeyid\",\n"
+ " \"private_key\": \"-----BEGIN PRIVATE KEY-----\\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggS"
+ "kAgEAAoIBAQC+K2hSuFpAdrJI\\nnCgcDz2M7t7bjdlsadsasad+fvRSW6TjNQZ3p5LLQY1kSZRqBqylRkzteMOyHg"
+ "aR\\n0Pmxh3ILCND5men43j3h4eDbrhQBuxfEMalkG92sL+PNQSETY2tnvXryOvmBRwa/\\nQP/9dJfIkIDJ9Fw9N4"
+ "Bhhhp6mCcRpdQjV38H7JsyJ7lih/oNjECgYAt\\nknddadwkwewcVxHFhcZJO+XWf6ofLUXpRwiTZakGMn8EE1uVa2"
+ "LgczOjwWHGi99MFjxSer5m9\\n1tCa3/KEGKiS/YL71JvjwX3mb+cewlkcmweBKZHM2JPTk0ZednFSpVZMtycjkbLa"
+ "\\ndYOS8V85AgMBewECggEBAKksaldajfDZDV6nGqbFjMiizAKJolr/M3OQw16K6o3/\\n0S31xIe3sSlgW0+UbYlF"
+ "4U8KifhManD1apVSC3csafaspP4RZUHFhtBywLO9pR5c\\nr6S5aLp+gPWFyIp1pfXbWGvc5VY/v9x7ya1VEa6rXvL"
+ "sKupSeWAW4tMj3eo/64ge\\nsdaceaLYw52KeBYiT6+vpsnYrEkAHO1fF/LavbLLOFJmFTMxmsNaG0tuiJHgjshB\\"
+ "n82DpMCbXG9YcCgI/DbzuIjsdj2JC1cascSP//3PmefWysucBQe7Jryb6NQtASmnv\\nCdDw/0jmZTEjpe4S1lxfHp"
+ "lAhHFtdgYTvyYtaLZiVVkCgYEA8eVpof2rceecw/I6\\n5ng1q3Hl2usdWV/4mZMvR0fOemacLLfocX6IYxT1zA1FF"
+ "JlbXSRsJMf/Qq39mOR2\\nSpW+hr4jCoHeRVYLgsbggtrevGmILAlNoqCMpGZ6vDmJpq6ECV9olliDvpPgWOP+\\nm"
+ "YPDreFBGxWvQrADNbRt2dmGsrsCgYEAyUHqB2wvJHFqdmeBsaacewzV8x9WgmeX\\ngUIi9REwXlGDW0Mz50dxpxcK"
+ "CAYn65+7TCnY5O/jmL0VRxU1J2mSWyWTo1C+17L0\\n3fUqjxL1pkefwecxwecvC+gFFYdJ4CQ/MHHXU81Lwl1iWdF"
+ "Cd2UoGddYaOF+KNeM\\nHC7cmqra+JsCgYEAlUNywzq8nUg7282E+uICfCB0LfwejuymR93CtsFgb7cRd6ak\\nECR"
+ "8FGfCpH8ruWJINllbQfcHVCX47ndLZwqv3oVFKh6pAS/vVI4dpOepP8++7y1u\\ncoOvtreXCX6XqfrWDtKIvv0vjl"
+ "HBhhhp6mCcRpdQjV38H7JsyJ7lih/oNjECgYAt\\nkndj5uNl5SiuVxHFhcZJO+XWf6ofLUregtevZakGMn8EE1uVa"
+ "2AY7eafmoU/nZPT\\n00YB0TBATdCbn/nBSuKDESkhSg9s2GEKQZG5hBmL5uCMfo09z3SfxZIhJdlerreP\\nJ7gSi"
+ "dI12N+EZxYd4xIJh/HFDgp7RRO87f+WJkofMQKBgGTnClK1VMaCRbJZPriw\\nEfeFCoOX75MxKwXs6xgrw4W//AYG"
+ "GUjDt83lD6AZP6tws7gJ2IwY/qP7+lyhjEqN\\nHtfPZRGFkGZsdaksdlaksd323423d+15/UvrlRSFPNj1tWQmNKk"
+ "XyRDW4IG1Oa2p\\nrALStNBx5Y9t0/LQnFI4w3aG\\n-----END PRIVATE KEY-----\\n\",\n"
+ " \"project_id\": \"someprojectid\",\n"
+ " \"client_email\": \"someclientid@developer.gserviceaccount.com\",\n"
+ " \"client_id\": \"someclientid.apps.googleusercontent.com\",\n"
+ " \"type\": \"service_account\",\n"
+ " \"quota_project_id\": \"some-quota-project-id\"\n"
Comment thread
codyoss marked this conversation as resolved.
+ "}";

static {
credentials = loadCredentials(JSON_KEY);
credentialsWithProjectId = loadCredentials(JSON_KEY_PROJECT_ID);
credentialsWithQuotaProject = loadCredentials(JSON_KEY_QUOTA_PROJECT_ID);
}

static GoogleCredentials loadCredentials(String credentialFile) {
try {
InputStream keyStream = new ByteArrayInputStream(JSON_KEY_PROJECT_ID.getBytes());
credentialsWithProjectId = GoogleCredentials.fromStream(keyStream);
InputStream keyStream = new ByteArrayInputStream(credentialFile.getBytes());
return GoogleCredentials.fromStream(keyStream);
} catch (IOException e) {
fail("Couldn't create fake JSON credentials.");
}
return null;
}

private static final ApiClock TEST_CLOCK = new TestClock();
Expand All@@ -138,6 +168,7 @@ public class ServiceOptionsTest {
.setHost("host")
.setProjectId("project-id")
.setRetrySettings(ServiceOptions.getNoRetrySettings())
.setQuotaProjectId("quota-project-id")
.build();
private static final TestServiceOptions OPTIONS_NO_CREDENTIALS =
TestServiceOptions.newBuilder()
Expand All@@ -146,6 +177,7 @@ public class ServiceOptionsTest {
.setHost("host")
.setProjectId("project-id")
.setRetrySettings(ServiceOptions.getNoRetrySettings())
.setQuotaProjectId("quota-project-id")
.build();
private static final TestServiceOptions DEFAULT_OPTIONS =
TestServiceOptions.newBuilder().setProjectId("project-id").build();
Expand DownExpand Up@@ -283,6 +315,39 @@ public void testBuilder() {
assertSame(ServiceOptions.getDefaultRetrySettings(), DEFAULT_OPTIONS.getRetrySettings());
}

@Test
public void testBuilder_quotaProjectServiceOptionTakesPrecedence() {
TestServiceOptions noCredsWithQuotaProject =
TestServiceOptions.newBuilder()
.setCredentials(NoCredentials.getInstance())
.setProjectId("project-id")
.setQuotaProjectId("quota-project-id")
.build();
TestServiceOptions quotaProjectCredsWithQuotaProject =
TestServiceOptions.newBuilder()
.setQuotaProjectId("quota-project-id")
.setCredentials(credentialsWithQuotaProject)
.build();
TestServiceOptions quotaProjectCredsWithQuotaProject2 =
TestServiceOptions.newBuilder()
.setCredentials(credentialsWithQuotaProject)
.setQuotaProjectId("quota-project-id")
.build();
TestServiceOptions quotaProjectCreds =
TestServiceOptions.newBuilder().setCredentials(credentialsWithQuotaProject).build();
TestServiceOptions none =
TestServiceOptions.newBuilder()
.setCredentials(NoCredentials.getInstance())
.setProjectId("project-id")
.build();

assertEquals("quota-project-id", noCredsWithQuotaProject.getQuotaProjectId());
assertEquals("quota-project-id", quotaProjectCredsWithQuotaProject.getQuotaProjectId());
assertEquals("quota-project-id", quotaProjectCredsWithQuotaProject2.getQuotaProjectId());
assertEquals("some-quota-project-id", quotaProjectCreds.getQuotaProjectId());
assertEquals(null, none.getQuotaProjectId());
}

@Test
public void testBuilderNoCredentials() {
assertEquals(NoCredentials.getInstance(), OPTIONS_NO_CREDENTIALS.getCredentials());
Expand All@@ -293,6 +358,7 @@ public void testBuilderNoCredentials() {
assertEquals("host", OPTIONS_NO_CREDENTIALS.getHost());
assertEquals("project-id", OPTIONS_NO_CREDENTIALS.getProjectId());
assertSame(ServiceOptions.getNoRetrySettings(), OPTIONS_NO_CREDENTIALS.getRetrySettings());
assertEquals("quota-project-id", OPTIONS.getQuotaProjectId());
}

@Test
Expand DownExpand Up@@ -372,7 +438,8 @@ public void testGetServiceAccountProjectId() throws Exception {
Files.write("{\"project_id\":\"my-project-id\"}".getBytes(), credentialsFile);

assertEquals(
"my-project-id", ServiceOptions.getServiceAccountProjectId(credentialsFile.getPath()));
"my-project-id",
ServiceOptions.getValueFromCredentialsFile(credentialsFile.getPath(), "project_id"));
}

@Test
Expand All@@ -381,14 +448,14 @@ public void testGetServiceAccountProjectId_badJson() throws Exception {
credentialsFile.deleteOnExit();
Files.write("asdfghj".getBytes(), credentialsFile);

assertNull(ServiceOptions.getServiceAccountProjectId(credentialsFile.getPath()));
assertNull(ServiceOptions.getValueFromCredentialsFile(credentialsFile.getPath(), "project_id"));
}

@Test
public void testGetServiceAccountProjectId_nonExistentFile() throws Exception {
File credentialsFile = new File("/doesnotexist");

assertNull(ServiceOptions.getServiceAccountProjectId(credentialsFile.getPath()));
assertNull(ServiceOptions.getValueFromCredentialsFile(credentialsFile.getPath(), "project_id"));
}

@Test
Expand Down
4 changes: 2 additions & 2 deletions pom.xml
Original file line numberDiff line numberDiff line change
Expand Up@@ -152,11 +152,11 @@
<github.global.server>github</github.global.server>
<site.installationModule>google-cloud-core-parent</site.installationModule>

<gax.version>1.51.0</gax.version>
<gax.version>1.52.0</gax.version>
<google.api-common.version>1.8.1</google.api-common.version>
<google.common-protos.version>1.17.0</google.common-protos.version>
<google.iam.version>0.13.0</google.iam.version>
<google.auth.version>0.18.0</google.auth.version>
<google.auth.version>0.19.0</google.auth.version>
<google.api.version>1.30.6</google.api.version>
<google.http.version>1.33.0</google.http.version>
<grpc.version>1.25.0</grpc.version>
Expand Down