Skip to content
This repository has been archived by the owner on Dec 3, 2023. It is now read-only.

feat: support setting ServiceOption for quota project #92

Merged
merged 7 commits into from Dec 13, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
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 Down Expand 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 Down Expand 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 Down Expand Up @@ -212,6 +216,10 @@ public B setCredentials(Credentials credentials) {
if (this.projectId == null && credentials instanceof ServiceAccountCredentials) {
this.projectId = ((ServiceAccountCredentials) credentials).getProjectId();
}

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

Expand Down Expand 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 Down Expand 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 Down Expand 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 Down Expand 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 Down Expand 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;
}
}
Expand Up @@ -117,7 +117,8 @@ public class ServiceOptionsTest {
+ " \"project_id\": \"someprojectid\",\n"
+ " \"client_email\": \"someclientid@developer.gserviceaccount.com\",\n"
+ " \"client_id\": \"someclientid.apps.googleusercontent.com\",\n"
+ " \"type\": \"service_account\"\n"
+ " \"type\": \"service_account\",\n"
+ " \"quota_project_id\": \"some-quota-project-id\"\n"
codyoss marked this conversation as resolved.
Show resolved Hide resolved
+ "}";
private static GoogleCredentials credentialsWithProjectId;

Expand All @@ -138,6 +139,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 Down Expand Up @@ -281,6 +283,7 @@ public void testBuilder() {
assertSame(CurrentMillisClock.getDefaultClock(), DEFAULT_OPTIONS.getClock());
assertEquals("https://www.googleapis.com", DEFAULT_OPTIONS.getHost());
assertSame(ServiceOptions.getDefaultRetrySettings(), DEFAULT_OPTIONS.getRetrySettings());
assertEquals("quota-project-id", OPTIONS.getQuotaProjectId());
}

@Test
Expand All @@ -293,6 +296,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 All @@ -308,6 +312,13 @@ public void testBuilderServiceAccount_setsProjectId() {
assertEquals("someprojectid", options.getProjectId());
}

@Test
public void testBuilderServiceAccount_setsQuotaProjectId() {
TestServiceOptions options =
TestServiceOptions.newBuilder().setCredentials(credentialsWithProjectId).build();
assertEquals("some-quota-project-id", options.getQuotaProjectId());
}

@Test
public void testBuilderServiceAccount_explicitSetProjectIdBefore() {
TestServiceOptions options =
Expand Down Expand Up @@ -372,7 +383,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 +393,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
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