Skip to content

Commit

Permalink
Use ide login library (#486)
Browse files Browse the repository at this point in the history
* Use the ide-login library
* Fail login if it seems already in progress
* Convert GoogleLoginService into OGSi service
* Use OSGi Declarative Services
* Pull jackson-core-asl from Maven Central
* Refactor / enhance code design
* Make ShellProvider return shells dynamically
  • Loading branch information
chanseokoh authored Aug 5, 2016
1 parent 03dc122 commit ae58737
Show file tree
Hide file tree
Showing 32 changed files with 638 additions and 378 deletions.
1 change: 1 addition & 0 deletions eclipse/ide-target-platform/category.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@
<bundle id="com.google.guava" version="17.0" />
<bundle id="com.google.gson" version="2.6.2" />
<bundle id="com.google.cloud.tools.app.lib" version="0.0.0" />
<bundle id="jackson-core-asl" version="1.9.13" />
</site>
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,11 @@
version="0.0.0"
unpack="false"/>

<plugin
id="jackson-core-asl"
download-size="0"
install-size="0"
version="1.9.13"
unpack="false"/>

</feature>
Original file line number Diff line number Diff line change
@@ -1,7 +1,30 @@
/*******************************************************************************
* Copyright 2016 Google Inc. All Rights Reserved.
*
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the Eclipse Public License v1.0 which
* accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* 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.
*******************************************************************************/

package com.google.cloud.tools.eclipse.appengine.deploy.standard;

import java.io.IOException;
import java.nio.file.Files;
import com.google.api.client.auth.oauth2.Credential;
import com.google.cloud.tools.eclipse.appengine.deploy.AppEngineProjectDeployer;
import com.google.cloud.tools.eclipse.appengine.deploy.CleanupOldDeploysJob;
import com.google.cloud.tools.eclipse.appengine.deploy.Messages;
import com.google.cloud.tools.eclipse.appengine.login.IGoogleLoginService;
import com.google.cloud.tools.eclipse.ui.util.ProjectFromSelectionHelper;
import com.google.cloud.tools.eclipse.util.FacetedProjectHelper;
import com.google.cloud.tools.eclipse.util.ServiceUtils;
import com.google.cloud.tools.eclipse.util.status.StatusUtil;
import com.google.common.annotations.VisibleForTesting;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
Expand All @@ -12,19 +35,9 @@
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.jface.window.IShellProvider;
import org.eclipse.jface.window.SameShellProvider;
import org.eclipse.ui.handlers.HandlerUtil;

import com.google.api.client.auth.oauth2.Credential;
import com.google.cloud.tools.eclipse.appengine.deploy.AppEngineProjectDeployer;
import com.google.cloud.tools.eclipse.appengine.deploy.CleanupOldDeploysJob;
import com.google.cloud.tools.eclipse.appengine.deploy.Messages;
import com.google.cloud.tools.eclipse.appengine.login.GoogleLoginService;
import com.google.cloud.tools.eclipse.ui.util.ProjectFromSelectionHelper;
import com.google.cloud.tools.eclipse.util.FacetedProjectHelper;
import com.google.cloud.tools.eclipse.util.status.StatusUtil;
import com.google.common.annotations.VisibleForTesting;
import java.io.IOException;
import java.nio.file.Files;

/**
* Command handler to deploy an App Engine web application project to App Engine Standard.
Expand All @@ -35,22 +48,22 @@
public class StandardDeployCommandHandler extends AbstractHandler {

private ProjectFromSelectionHelper helper;

public StandardDeployCommandHandler() {
this(new FacetedProjectHelper());
}

@VisibleForTesting
StandardDeployCommandHandler(FacetedProjectHelper facetedProjectHelper) {
this.helper = new ProjectFromSelectionHelper(facetedProjectHelper);
}

@Override
public Object execute(ExecutionEvent event) throws ExecutionException {
try {
IProject project = helper.getProject(event);
if (project != null) {
launchDeployJob(project, new SameShellProvider(HandlerUtil.getActiveShell(event)));
launchDeployJob(project, ServiceUtils.getService(event, IGoogleLoginService.class));
}
// return value must be null, reserved for future use
return null;
Expand All @@ -59,10 +72,11 @@ public Object execute(ExecutionEvent event) throws ExecutionException {
}
}

private void launchDeployJob(IProject project, IShellProvider shellProvider) throws IOException, CoreException {
private void launchDeployJob(IProject project, IGoogleLoginService loginService)
throws IOException, CoreException {
IPath workDirectory = createWorkDirectory();
Credential credential = login(shellProvider);
Credential credential = login(loginService);

StandardDeployJob deploy =
new StandardDeployJob(new ExplodedWarPublisher(),
new StandardProjectStaging(),
Expand All @@ -88,8 +102,8 @@ private IPath createWorkDirectory() throws IOException {
return workDirectory;
}

private Credential login(IShellProvider shellProvider) throws IOException, CoreException {
Credential credential = new GoogleLoginService().getActiveCredential(shellProvider);
private Credential login(IGoogleLoginService loginService) throws CoreException {
Credential credential = loginService.getActiveCredential();
if (credential == null) {
throw new CoreException(StatusUtil.error(getClass(), Messages.getString("login.failed")));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Fragment-Host: com.google.cloud.tools.eclipse.appengine.login
Require-Bundle: org.hamcrest;bundle-version="1.1.0",
org.junit;bundle-version="4.12.0"
Import-Package: org.mockito;provider=google;version="1.10.19",
org.mockito.invocation;provider=google;version="1.10.19",
org.mockito.runners;provider=google;version="1.10.19",
org.mockito.stubbing;provider=google;version="1.10.19",
org.objenesis;provider=google;version="2.2.0"
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,17 @@
import org.junit.Test;

import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.jackson.JacksonFactory;
import com.google.gson.Gson;

public class CredentialHelperTest {

@Test
public void testCreateCredential() {
Credential credential = new CredentialHelper().createCredential("fake_access_token", "fake_refresh_token");

Assert.assertEquals(credential.getAccessToken(), "fake_access_token");
Assert.assertEquals(credential.getRefreshToken(), "fake_refresh_token");
}

@Test
public void testGetJsonCredential() {
CredentialHelper credentialHelper = new CredentialHelper();
Credential credential = credentialHelper.createCredential("fake_access_token", "fake_refresh_token");
String jsonCredential = credentialHelper.toJson(credential);
Credential credential = createCredential("fake_access_token", "fake_refresh_token");
String jsonCredential = new CredentialHelper().toJson(credential);

CredentialType credentialType = new Gson().fromJson(jsonCredential, CredentialType.class);
Assert.assertEquals(credentialType.client_id, Constants.getOAuthClientId());
Expand All @@ -35,4 +29,15 @@ private class CredentialType {
private String refresh_token;
private String type;
};

private Credential createCredential(String accessToken, String refreshToken) {
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(new NetHttpTransport())
.setJsonFactory(new JacksonFactory())
.setClientSecrets(Constants.getOAuthClientId(), Constants.getOAuthClientSecret())
.build();
credential.setAccessToken(accessToken);
credential.setRefreshToken(refreshToken);
return credential;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*******************************************************************************
* Copyright 2016 Google Inc. All Rights Reserved.
*
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the Eclipse Public License v1.0 which
* accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* 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.
*******************************************************************************/

package com.google.cloud.tools.eclipse.appengine.login;

import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.google.cloud.tools.eclipse.appengine.login.ui.LoginServiceUi;
import com.google.cloud.tools.ide.login.LoggerFacade;
import com.google.cloud.tools.ide.login.OAuthData;
import com.google.cloud.tools.ide.login.OAuthDataStore;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import java.util.Arrays;
import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;

@RunWith(MockitoJUnitRunner.class)
public class GoogleLoginServiceTest {

@Mock private OAuthDataStore dataStore;
@Mock private OAuthData savedOAuthData;
@Mock private LoginServiceUi uiFacade;
@Mock private LoggerFacade loggerFacade;

private static final SortedSet<String> OAUTH_SCOPES = Collections.unmodifiableSortedSet(
new TreeSet<>(Arrays.asList(
"email",
"https://www.googleapis.com/auth/cloud-platform"
)));

@Before
public void setUp() {
when(dataStore.loadOAuthData()).thenReturn(savedOAuthData);
}

@Test
public void testGoogleLoginService_clearSavedCredentialIfNullRefreshToken() {
when(savedOAuthData.getRefreshToken()).thenReturn(null);

GoogleLoginService loginService = new GoogleLoginService(dataStore, uiFacade, loggerFacade);
Assert.assertNull(loginService.getCachedActiveCredential());
}

@Test
public void testGoogleLoginService_clearSavedCredentialIfScopesChanged() {
// Persisted credential in the data store has an out-dated scopes.
SortedSet<String> newScope = new TreeSet<String>(Arrays.asList("new scope"));
when(savedOAuthData.getStoredScopes()).thenReturn(newScope);
when(savedOAuthData.getRefreshToken()).thenReturn("fake_refresh_token");

GoogleLoginService loginService = new GoogleLoginService(dataStore, uiFacade, loggerFacade);
Assert.assertNull(loginService.getCachedActiveCredential());
}

@Test
public void testGoogleLoginService_restoreSavedCredential() {
// Persisted credential in the data store is valid.
when(savedOAuthData.getStoredScopes()).thenReturn(OAUTH_SCOPES);
when(savedOAuthData.getRefreshToken()).thenReturn("fake_refresh_token");

GoogleLoginService loginService = new GoogleLoginService(dataStore, uiFacade, loggerFacade);
verify(dataStore, never()).clearStoredOAuthData();
Assert.assertNotNull(loginService.getCachedActiveCredential());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*******************************************************************************
* Copyright 2016 Google Inc. All Rights Reserved.
*
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the Eclipse Public License v1.0 which
* accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* 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.
*******************************************************************************/

package com.google.cloud.tools.eclipse.appengine.login;

import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.google.cloud.tools.ide.login.OAuthData;

import org.eclipse.e4.core.contexts.IEclipseContext;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class TransientOAuthDataStoreTest {

@Mock private IEclipseContext eclipseContext;

@Test
public void testLoadOAuthData_emptyStoreReturnsNonNullOAuthData() {
when(eclipseContext.get(anyString())).thenReturn(null);

OAuthData oAuthData = new TransientOAuthDataStore(eclipseContext).loadOAuthData();
Assert.assertNotNull(oAuthData);
Assert.assertEquals(null, oAuthData.getAccessToken());
Assert.assertEquals(null, oAuthData.getRefreshToken());
Assert.assertEquals(null, oAuthData.getStoredEmail());
Assert.assertEquals(0, oAuthData.getAccessTokenExpiryTime());
}

@Test
public void testSaveAndLoadOAuthData() {
OAuthData inputData = mock(OAuthData.class);
TransientOAuthDataStore dataStore = new TransientOAuthDataStore(eclipseContext);
dataStore.saveOAuthData(inputData);
dataStore.loadOAuthData();

ArgumentCaptor<OAuthData> argumentCaptor = ArgumentCaptor.forClass(OAuthData.class);
verify(eclipseContext).set(anyString(), argumentCaptor.capture());
verify(eclipseContext).get(anyString());
Assert.assertEquals(inputData, argumentCaptor.getValue());
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="generated-sources"/>
<classpathentry exported="true" kind="lib" path="lib/com.google.cloud.tools.ide.login-0.0.0-SNAPSHOT.jar"/>
<classpathentry exported="true" kind="lib" path="lib/google-api-client-1.22.0.jar"/>
<classpathentry exported="true" kind="lib" path="lib/google-api-services-oauth2-v2-rev114-1.22.0.jar"/>
<classpathentry exported="true" kind="lib" path="lib/google-http-client-1.22.0.jar"/>
<classpathentry exported="true" kind="lib" path="lib/google-http-client-jackson2-1.22.0.jar"/>
<classpathentry exported="true" kind="lib" path="lib/google-http-client-jackson-1.22.0.jar"/>
<classpathentry exported="true" kind="lib" path="lib/google-oauth-client-1.22.0.jar"/>
<classpathentry exported="true" kind="lib" path="lib/jackson-core-2.1.3.jar"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>
Loading

0 comments on commit ae58737

Please sign in to comment.