Skip to content

Commit 78772ab

Browse files
authored
Fixes #1439 - Verify if we need to handle proxy settings in com.google.cloud.tools.eclipse.projectselector.GoogleApiFactory (#1513)
* make GoogleApiFactory a service, create proxy based on info obtained from IProxyService * finish turning GoogleApiFactory into a service, add some tests * make transport access/modification synchronized, create transport per URL * make IProxyService an optional dependency, handle if it's null, handle SOCKS proxy, error on http schema * remove proxy change listener when unsetting proxy service * fix proxy selection, rename local and loop variables * use Cache instead of Map for transports in GoogleApiFactory * simplify build.properties * add more tests to Google APIs bundle * review comments addressed * fill in javadoc for IGoogleApiFactory, make jsonFactory final in GoogleApiFactory
1 parent 39c0969 commit 78772ab

25 files changed

Lines changed: 939 additions & 132 deletions

File tree

plugins/com.google.cloud.tools.eclipse.appengine.deploy.ui/src/com/google/cloud/tools/eclipse/appengine/deploy/ui/DeployPreferencesDialog.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import com.google.cloud.tools.appengine.cloudsdk.CloudSdkNotFoundException;
2323
import com.google.cloud.tools.appengine.cloudsdk.CloudSdkOutOfDateException;
2424
import com.google.cloud.tools.eclipse.appengine.ui.AppEngineImages;
25-
import com.google.cloud.tools.eclipse.googleapis.GoogleApiFactory;
25+
import com.google.cloud.tools.eclipse.googleapis.IGoogleApiFactory;
2626
import com.google.cloud.tools.eclipse.login.IGoogleLoginService;
2727
import com.google.cloud.tools.eclipse.projectselector.ProjectRepository;
2828
import com.google.common.base.Preconditions;
@@ -56,14 +56,19 @@ public class DeployPreferencesDialog extends TitleAreaDialog {
5656
private IProject project;
5757
private IGoogleLoginService loginService;
5858

59+
private IGoogleApiFactory googleApiFactory;
60+
5961
public DeployPreferencesDialog(Shell parentShell, IProject project,
60-
IGoogleLoginService loginService) {
62+
IGoogleLoginService loginService,
63+
IGoogleApiFactory googleApiFactory) {
6164
super(parentShell);
6265

6366
Preconditions.checkNotNull(project, "project is null");
6467
Preconditions.checkNotNull(loginService, "loginService is null");
68+
Preconditions.checkNotNull(googleApiFactory, "googleApiFactory is null");
6569
this.project = project;
6670
this.loginService = loginService;
71+
this.googleApiFactory = googleApiFactory;
6772
}
6873

6974
@Override
@@ -92,7 +97,7 @@ protected Control createDialogArea(final Composite parent) {
9297
Composite container = new Composite(dialogArea, SWT.NONE);
9398
content = new StandardDeployPreferencesPanel(container, project, loginService,
9499
getLayoutChangedHandler(), true /* requireValues */,
95-
new ProjectRepository(new GoogleApiFactory()));
100+
new ProjectRepository(googleApiFactory));
96101
GridDataFactory.fillDefaults().grab(true, false).applyTo(content);
97102

98103
// we pull in Dialog's content margins which are zeroed out by TitleAreaDialog

plugins/com.google.cloud.tools.eclipse.appengine.deploy.ui/src/com/google/cloud/tools/eclipse/appengine/deploy/ui/DeployPropertyPage.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
import com.google.cloud.tools.eclipse.appengine.facets.AppEngineFlexFacet;
2020
import com.google.cloud.tools.eclipse.appengine.facets.AppEngineStandardFacet;
21-
import com.google.cloud.tools.eclipse.googleapis.GoogleApiFactory;
21+
import com.google.cloud.tools.eclipse.googleapis.IGoogleApiFactory;
2222
import com.google.cloud.tools.eclipse.login.IGoogleLoginService;
2323
import com.google.cloud.tools.eclipse.projectselector.ProjectRepository;
2424
import com.google.cloud.tools.eclipse.util.AdapterUtil;
@@ -149,14 +149,17 @@ public void setMessage(String newMessage, int newType) {
149149

150150
private DeployPreferencesPanel getPreferencesPanel(IProject project,
151151
IFacetedProject facetedProject, Composite container) {
152+
152153
IGoogleLoginService loginService =
153154
PlatformUI.getWorkbench().getService(IGoogleLoginService.class);
155+
IGoogleApiFactory googleApiFactory =
156+
PlatformUI.getWorkbench().getService(IGoogleApiFactory.class);
154157

155158
if (AppEngineStandardFacet.hasAppEngineFacet(facetedProject)) {
156159
setTitle(Messages.getString("standard.page.title"));
157160
return new StandardDeployPreferencesPanel(
158161
container, project, loginService, getLayoutChangedHandler(), false /* requireValues */,
159-
new ProjectRepository(new GoogleApiFactory()));
162+
new ProjectRepository(googleApiFactory));
160163
} else if (AppEngineFlexFacet.hasAppEngineFacet(facetedProject)) {
161164
setTitle(Messages.getString("flex.page.title"));
162165
return new FlexDeployPreferencesPanel(container, project);

plugins/com.google.cloud.tools.eclipse.appengine.deploy.ui/src/com/google/cloud/tools/eclipse/appengine/deploy/ui/StandardDeployPreferencesPanel.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@
6363
import org.eclipse.jface.layout.GridDataFactory;
6464
import org.eclipse.jface.layout.GridLayoutFactory;
6565
import org.eclipse.swt.SWT;
66+
import org.eclipse.swt.events.ControlAdapter;
67+
import org.eclipse.swt.events.ControlEvent;
6668
import org.eclipse.swt.graphics.Font;
6769
import org.eclipse.swt.layout.GridData;
6870
import org.eclipse.swt.layout.GridLayout;

plugins/com.google.cloud.tools.eclipse.appengine.deploy.ui/src/com/google/cloud/tools/eclipse/appengine/deploy/ui/standard/StandardDeployCommandHandler.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.google.cloud.tools.eclipse.appengine.deploy.ui.DeployConsole;
2626
import com.google.cloud.tools.eclipse.appengine.deploy.ui.DeployPreferencesDialog;
2727
import com.google.cloud.tools.eclipse.appengine.deploy.ui.Messages;
28+
import com.google.cloud.tools.eclipse.googleapis.IGoogleApiFactory;
2829
import com.google.cloud.tools.eclipse.login.IGoogleLoginService;
2930
import com.google.cloud.tools.eclipse.sdk.ui.MessageConsoleWriterOutputLineListener;
3031
import com.google.cloud.tools.eclipse.ui.util.MessageConsoleUtilities;
@@ -86,8 +87,10 @@ public Object execute(ExecutionEvent event) throws ExecutionException {
8687
}
8788

8889
IGoogleLoginService loginService = ServiceUtils.getService(event, IGoogleLoginService.class);
90+
IGoogleApiFactory googleApiFactory = ServiceUtils.getService(event, IGoogleApiFactory.class);
8991
DeployPreferencesDialog dialog =
90-
new DeployPreferencesDialog(HandlerUtil.getActiveShell(event), project, loginService);
92+
new DeployPreferencesDialog(HandlerUtil.getActiveShell(event), project, loginService,
93+
googleApiFactory);
9194
if (dialog.open() == Window.OK) {
9295
launchDeployJob(project, dialog.getCredential());
9396
}

plugins/com.google.cloud.tools.eclipse.googleapis.test/META-INF/MANIFEST.MF

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Bundle-Vendor: Google Inc.
88
Bundle-Version: 0.1.0.qualifier
99
Fragment-Host: com.google.cloud.tools.eclipse.googleapis
1010
Require-Bundle: org.junit;bundle-version="4.12.0"
11-
Import-Package: org.mockito;provider=google;version="1.10.19",
11+
Import-Package: com.google.cloud.tools.eclipse.test.util.http,
12+
org.mockito;provider=google;version="1.10.19",
1213
org.mockito.runners;provider=google;version="1.10.19",
1314
org.mockito.stubbing;provider=google;version="1.10.19"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/*
2+
* Copyright 2017 Google Inc.
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+
*/
16+
17+
package com.google.cloud.tools.eclipse.googleapis.internal;
18+
19+
import static org.hamcrest.CoreMatchers.containsString;
20+
import static org.junit.Assert.assertThat;
21+
import static org.mockito.Matchers.any;
22+
import static org.mockito.Mockito.doNothing;
23+
import static org.mockito.Mockito.mock;
24+
import static org.mockito.Mockito.never;
25+
import static org.mockito.Mockito.times;
26+
import static org.mockito.Mockito.verify;
27+
import static org.mockito.Mockito.when;
28+
29+
import com.google.api.client.auth.oauth2.Credential;
30+
import com.google.api.client.http.HttpTransport;
31+
import com.google.api.client.json.JsonFactory;
32+
import com.google.api.services.appengine.v1.Appengine.Apps;
33+
import com.google.api.services.cloudresourcemanager.CloudResourceManager.Projects;
34+
import com.google.cloud.tools.eclipse.googleapis.GoogleApiException;
35+
import com.google.cloud.tools.eclipse.util.CloudToolsInfo;
36+
import com.google.common.cache.LoadingCache;
37+
import java.io.IOException;
38+
import java.util.concurrent.ExecutionException;
39+
import org.eclipse.core.net.proxy.IProxyChangeEvent;
40+
import org.eclipse.core.net.proxy.IProxyChangeListener;
41+
import org.eclipse.core.net.proxy.IProxyService;
42+
import org.junit.Before;
43+
import org.junit.Test;
44+
import org.junit.runner.RunWith;
45+
import org.mockito.ArgumentCaptor;
46+
import org.mockito.Captor;
47+
import org.mockito.Mock;
48+
import org.mockito.runners.MockitoJUnitRunner;
49+
50+
@RunWith(MockitoJUnitRunner.class)
51+
public class GoogleApiFactoryTest {
52+
53+
@Mock private JsonFactory jsonFactory;
54+
@Mock private IProxyService proxyService;
55+
@Mock private Credential credential;
56+
@Mock LoadingCache<GoogleApiUrl, HttpTransport> transportCache;
57+
@Mock private ProxyFactory proxyFactory;
58+
@Captor private ArgumentCaptor<IProxyChangeListener> proxyChangeListenerCaptor =
59+
ArgumentCaptor.forClass(IProxyChangeListener.class);
60+
61+
private GoogleApiFactory googleApiFactory;
62+
63+
@Before
64+
public void setUp() throws ExecutionException {
65+
googleApiFactory = new GoogleApiFactory(proxyFactory);
66+
when(transportCache.get(any(GoogleApiUrl.class))).thenReturn(mock(HttpTransport.class));
67+
googleApiFactory.setTransportCache(transportCache);
68+
}
69+
70+
@Test
71+
public void testNewAppsApi_userAgentIsSet() throws IOException, GoogleApiException {
72+
Apps api = googleApiFactory.newAppsApi(mock(Credential.class));
73+
assertThat(api.get("").getRequestHeaders().getUserAgent(),
74+
containsString(CloudToolsInfo.USER_AGENT));
75+
}
76+
77+
@Test(expected = GoogleApiException.class)
78+
public void testNewAppsApi_transportCacheErrorTranslatedToGoogleApiException()
79+
throws ExecutionException, GoogleApiException {
80+
when(transportCache.get(any(GoogleApiUrl.class)))
81+
.thenThrow(new ExecutionException(new Exception("test")));
82+
googleApiFactory.newAppsApi(mock(Credential.class));
83+
}
84+
85+
@Test
86+
public void testNewProjectsApi_userAgentIsSet() throws IOException, GoogleApiException {
87+
Projects api = googleApiFactory.newProjectsApi(mock(Credential.class));
88+
assertThat(api.get("").getRequestHeaders().getUserAgent(),
89+
containsString(CloudToolsInfo.USER_AGENT));
90+
}
91+
92+
@Test(expected = GoogleApiException.class)
93+
public void testNewProjectsApi_transportCacheErrorTranslatedToGoogleApiException()
94+
throws ExecutionException, GoogleApiException {
95+
when(transportCache.get(any(GoogleApiUrl.class)))
96+
.thenThrow(new ExecutionException(new Exception("test")));
97+
googleApiFactory.newProjectsApi(mock(Credential.class));
98+
}
99+
100+
@Test
101+
public void testSetProxyService() {
102+
googleApiFactory.setProxyService(proxyService);
103+
104+
verify(proxyService).addProxyChangeListener(any(IProxyChangeListener.class));
105+
verify(proxyFactory).setProxyService(proxyService);
106+
verify(transportCache).invalidateAll();
107+
}
108+
109+
@Test
110+
public void testUnsetDifferentProxyService() {
111+
googleApiFactory.setProxyService(proxyService);
112+
googleApiFactory.unsetProxyService(mock(IProxyService.class));
113+
114+
verify(proxyService, never()).removeProxyChangeListener(any(IProxyChangeListener.class));
115+
verify(proxyFactory, never()).setProxyService(null);
116+
// called once when setProxyService() is called
117+
verify(transportCache).invalidateAll();
118+
}
119+
120+
@Test
121+
public void testSetAndUnsetProxyService() {
122+
googleApiFactory.setProxyService(proxyService);
123+
googleApiFactory.unsetProxyService(proxyService);
124+
125+
verify(proxyService).addProxyChangeListener(any(IProxyChangeListener.class));
126+
verify(proxyService).removeProxyChangeListener(any(IProxyChangeListener.class));
127+
verify(proxyFactory).setProxyService(proxyService);
128+
verify(proxyFactory).setProxyService(null);
129+
verify(transportCache, times(2)).invalidateAll();
130+
}
131+
132+
@Test
133+
public void testProxyChangeListenerInvalidatesCache() {
134+
doNothing().when(proxyService).addProxyChangeListener(proxyChangeListenerCaptor.capture());
135+
googleApiFactory.setProxyService(proxyService);
136+
verify(transportCache).invalidateAll();
137+
138+
proxyChangeListenerCaptor.getValue().proxyInfoChanged(mock(IProxyChangeEvent.class));
139+
verify(transportCache, times(2)).invalidateAll();
140+
}
141+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright 2017 Google Inc.
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+
*/
16+
17+
package com.google.cloud.tools.eclipse.googleapis.internal;
18+
19+
import static org.hamcrest.CoreMatchers.containsString;
20+
import static org.junit.Assert.assertThat;
21+
import static org.mockito.Matchers.any;
22+
import static org.mockito.Mockito.mock;
23+
import static org.mockito.Mockito.verify;
24+
import static org.mockito.Mockito.when;
25+
26+
import com.google.api.client.auth.oauth2.Credential;
27+
import com.google.api.client.http.HttpTransport;
28+
import com.google.api.client.json.JsonFactory;
29+
import com.google.api.services.appengine.v1.Appengine.Apps;
30+
import com.google.api.services.cloudresourcemanager.CloudResourceManager.Projects;
31+
import com.google.cloud.tools.eclipse.googleapis.GoogleApiException;
32+
import com.google.cloud.tools.eclipse.util.CloudToolsInfo;
33+
import com.google.common.cache.LoadingCache;
34+
import java.io.IOException;
35+
import java.net.URI;
36+
import java.util.concurrent.ExecutionException;
37+
import org.eclipse.core.net.proxy.IProxyChangeListener;
38+
import org.eclipse.core.net.proxy.IProxyData;
39+
import org.eclipse.core.net.proxy.IProxyService;
40+
import org.junit.Before;
41+
import org.junit.Test;
42+
import org.junit.runner.RunWith;
43+
import org.mockito.ArgumentCaptor;
44+
import org.mockito.Captor;
45+
import org.mockito.Mock;
46+
import org.mockito.runners.MockitoJUnitRunner;
47+
48+
@RunWith(MockitoJUnitRunner.class)
49+
public class GoogleApiFactoryWithProxyServerTest {
50+
51+
@Mock private JsonFactory jsonFactory;
52+
@Mock private IProxyService proxyService;
53+
@Mock private Credential credential;
54+
@Mock LoadingCache<GoogleApiUrl, HttpTransport> transportCache;
55+
@Captor private ArgumentCaptor<IProxyChangeListener> proxyListenerCaptor =
56+
ArgumentCaptor.forClass(IProxyChangeListener.class);
57+
58+
private GoogleApiFactory googleApiFactory;
59+
60+
@Before
61+
public void setUp() throws ExecutionException {
62+
googleApiFactory = new GoogleApiFactory();
63+
when(transportCache.get(any(GoogleApiUrl.class))).thenReturn(mock(HttpTransport.class));
64+
googleApiFactory.setTransportCache(transportCache);
65+
}
66+
67+
@Test
68+
public void testNewAppsApi_userAgentIsSet() throws IOException, GoogleApiException {
69+
Apps api = googleApiFactory.newAppsApi(mock(Credential.class));
70+
assertThat(api.get("").getRequestHeaders().getUserAgent(),
71+
containsString(CloudToolsInfo.USER_AGENT));
72+
}
73+
74+
@Test
75+
public void testNewProjectsApi_userAgentIsSet() throws IOException, GoogleApiException {
76+
Projects api = googleApiFactory.newProjectsApi(mock(Credential.class));
77+
assertThat(api.get("").getRequestHeaders().getUserAgent(),
78+
containsString(CloudToolsInfo.USER_AGENT));
79+
}
80+
81+
@Test
82+
public void testInit_ProxyChangeHandlerIsSet() {
83+
when(proxyService.select(any(URI.class))).thenReturn(new IProxyData[0]);
84+
googleApiFactory.setProxyService(proxyService);
85+
86+
googleApiFactory.init();
87+
88+
verify(proxyService).addProxyChangeListener(any(IProxyChangeListener.class));
89+
}
90+
}

0 commit comments

Comments
 (0)