From 1ff321b62d2947df203ea788d5b19785be8d9f21 Mon Sep 17 00:00:00 2001 From: Dieter Hubau Date: Tue, 29 Mar 2016 11:22:15 +0200 Subject: [PATCH 1/3] Added support for Redis as a backing datastore for social connections --- build.gradle | 11 +- gradle.properties | 2 + .../redis/RedisConnectionRepository.java | 175 +++++++++++++++++ .../redis/RedisUsersConnectionRepository.java | 41 ++++ .../redis/data/SocialRedisConnection.java | 125 ++++++++++++ .../data/SocialRedisConnectionRepository.java | 21 ++ .../social/connect/redis/RedisAvailable.java | 12 ++ .../connect/redis/RedisAvailableRule.java | 70 +++++++ ...sConnectionRepositoryIntegrationTests.java | 182 ++++++++++++++++++ 9 files changed, 635 insertions(+), 4 deletions(-) create mode 100644 spring-social-core/src/main/java/org/springframework/social/connect/redis/RedisConnectionRepository.java create mode 100644 spring-social-core/src/main/java/org/springframework/social/connect/redis/RedisUsersConnectionRepository.java create mode 100644 spring-social-core/src/main/java/org/springframework/social/connect/redis/data/SocialRedisConnection.java create mode 100644 spring-social-core/src/main/java/org/springframework/social/connect/redis/data/SocialRedisConnectionRepository.java create mode 100644 spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisAvailable.java create mode 100644 spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisAvailableRule.java create mode 100644 spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisConnectionRepositoryIntegrationTests.java diff --git a/build.gradle b/build.gradle index 8bbb1c283..7be34ea21 100644 --- a/build.gradle +++ b/build.gradle @@ -54,7 +54,7 @@ configure(allprojects) { ] sourceSets.test.resources.srcDirs = [ - "src/test/resources", + "src/test/resources", "src/test/java" ] @@ -148,7 +148,7 @@ configure(subprojects) { subproject -> archives javadocJar } - configurations { + configurations { springReleaseTestRuntime.extendsFrom testRuntime springSnapshotTestRuntime.extendsFrom testRuntime } @@ -195,6 +195,7 @@ project("spring-social-core") { description = "Foundational module containing the ServiceProvider Connect Framework and Service API invocation support." dependencies { compile("org.springframework:spring-jdbc:$springVersion", optional) + compile("org.springframework.data:spring-data-redis:$springDataRedisVersion", optional) compile("org.springframework:spring-web:$springVersion") compile("org.springframework.security:spring-security-crypto:$springSecurityVersion", optional) compile("org.apache.httpcomponents:httpclient:$httpComponentsVersion", optional) @@ -202,6 +203,8 @@ project("spring-social-core") { testCompile("org.springframework:spring-test:$springVersion") testCompile("javax.servlet:javax.servlet-api:$servletApiVersion", provided) testCompile("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion") + testCompile("org.apache.commons:commons-pool2:$apacheCommonsPoolVersion") + testCompile("redis.clients:jedis:2.8.1") } } @@ -256,7 +259,7 @@ configure(rootProject) { dependencies { // for integration tests } - + task api(type: Javadoc) { group = "Documentation" description = "Generates aggregated Javadoc API documentation." @@ -310,7 +313,7 @@ artifacts { archives dist archives project(':docs').docsZip archives project(':docs').schemaZip -} +} task wrapper(type: Wrapper) { gradleVersion = "1.12" diff --git a/gradle.properties b/gradle.properties index 9c7f88c76..ac1637d8a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,7 @@ h2Version=1.3.176 springSecurityVersion=3.2.9.RELEASE +springDataRedisVersion=1.7.0.RC1 +apacheCommonsPoolVersion=2.2 junitVersion=4.12 httpComponentsVersion=4.3.6 aspectjVersion=1.8.5 diff --git a/spring-social-core/src/main/java/org/springframework/social/connect/redis/RedisConnectionRepository.java b/spring-social-core/src/main/java/org/springframework/social/connect/redis/RedisConnectionRepository.java new file mode 100644 index 000000000..edeb7a18b --- /dev/null +++ b/spring-social-core/src/main/java/org/springframework/social/connect/redis/RedisConnectionRepository.java @@ -0,0 +1,175 @@ +package org.springframework.social.connect.redis; + +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.security.crypto.encrypt.TextEncryptor; +import org.springframework.social.connect.*; +import org.springframework.social.connect.redis.data.SocialRedisConnection; +import org.springframework.social.connect.redis.data.SocialRedisConnectionRepository; +import org.springframework.util.Assert; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +public class RedisConnectionRepository implements ConnectionRepository { + + private final ConnectionFactoryLocator connectionFactoryLocator; + private final TextEncryptor textEncryptor; + private final SocialRedisConnectionRepository socialRedisConnectionRepository; + private final String userId; + + public RedisConnectionRepository(final ConnectionFactoryLocator connectionFactoryLocator, final TextEncryptor textEncryptor, final SocialRedisConnectionRepository socialRedisConnectionRepository, final String userId) { + Assert.notNull(socialRedisConnectionRepository, "socialRedisConnectionRepository is required"); + Assert.notNull(userId, "userId is required"); + + this.userId = userId; + this.socialRedisConnectionRepository = socialRedisConnectionRepository; + this.connectionFactoryLocator = connectionFactoryLocator; + this.textEncryptor = textEncryptor; + } + + public MultiValueMap> findAllConnections() { + Iterable allConnections = socialRedisConnectionRepository.findByUserId(userId); + + final MultiValueMap> connections = new LinkedMultiValueMap>(); + Set registeredProviderIds = connectionFactoryLocator.registeredProviderIds(); + for (String registeredProviderId : registeredProviderIds) { + connections.put(registeredProviderId, new ArrayList>()); + } + + for (SocialRedisConnection connection : allConnections) { + connections.add(connection.getProviderId(), connectionMapper.mapConnection(connection)); + } + + return connections; + } + + public List> findConnections(String providerId) { + Iterable connections = socialRedisConnectionRepository.findByProviderId(providerId); + + List> providerConnections = new ArrayList>(); + for (SocialRedisConnection connection : connections) { + providerConnections.add(connectionMapper.mapConnection(connection)); + } + + return providerConnections; + } + + public List> findConnections(Class apiType) { + List connections = findConnections(getProviderId(apiType)); + return (List>) connections; + } + + public MultiValueMap> findConnectionsToUsers(MultiValueMap providerUserIds) { + return null; + } + + public Connection getConnection(ConnectionKey connectionKey) { + try { + return connectionMapper.mapConnection(socialRedisConnectionRepository.findOneByUserIdAndProviderIdAndProviderUserId(userId, connectionKey.getProviderId(), connectionKey.getProviderUserId())); + } catch (EmptyResultDataAccessException e) { + throw new NoSuchConnectionException(connectionKey); + } + } + + public Connection getConnection(Class apiType, String providerUserId) { + String providerId = getProviderId(apiType); + return (Connection) getConnection(new ConnectionKey(providerId, providerUserId)); + } + + public Connection getPrimaryConnection(Class apiType) { + String providerId = getProviderId(apiType); + Connection connection = (Connection) findPrimaryConnection(providerId); + if (connection == null) { + throw new NotConnectedException(providerId); + } + return connection; + } + + public Connection findPrimaryConnection(Class apiType) { + String providerId = getProviderId(apiType); + return (Connection) findPrimaryConnection(providerId); + } + + public void addConnection(Connection connection) { + try { + ConnectionData data = connection.createData(); + SocialRedisConnection redisConnection = new SocialRedisConnection(data.getProviderUserId(), userId, data.getProviderId(), data.getDisplayName(), data.getProfileUrl(), data.getImageUrl(), encrypt(data.getAccessToken()), encrypt(data.getSecret()), encrypt(data.getRefreshToken()), data.getExpireTime()); + socialRedisConnectionRepository.save(redisConnection); + } catch (Exception e) { + throw new DuplicateConnectionException(connection.getKey()); + } + } + + public void updateConnection(Connection connection) { + ConnectionData data = connection.createData(); + SocialRedisConnection redisConnection = socialRedisConnectionRepository.findOneByUserIdAndProviderIdAndProviderUserId(userId, data.getProviderId(), data.getProviderUserId()); + + redisConnection.setDisplayName(data.getDisplayName()); + redisConnection.setImageUrl(data.getImageUrl()); + redisConnection.setProfileUrl(data.getProfileUrl()); + redisConnection.setAccessToken(encrypt(data.getAccessToken())); + redisConnection.setSecret(encrypt(data.getSecret())); + redisConnection.setRefreshToken(encrypt(data.getRefreshToken())); + redisConnection.setExpireTime(data.getExpireTime()); + + socialRedisConnectionRepository.save(redisConnection); + } + + public void removeConnections(String providerId) { + Iterable connections = socialRedisConnectionRepository.findByUserIdAndProviderId(userId, providerId); + + for (SocialRedisConnection redisConnection : connections) { + socialRedisConnectionRepository.delete(redisConnection); + } + } + + public void removeConnection(ConnectionKey connectionKey) { + socialRedisConnectionRepository.deleteByUserIdAndProviderIdAndProviderUserId(userId, connectionKey.getProviderId(), connectionKey.getProviderUserId()); + } + + private final RedisConnectionMapper connectionMapper = new RedisConnectionMapper(); + + private final class RedisConnectionMapper { + + Connection mapConnection(final SocialRedisConnection redisConnection) { + ConnectionData connectionData = mapConnectionData(redisConnection); + ConnectionFactory connectionFactory = connectionFactoryLocator.getConnectionFactory(connectionData.getProviderId()); + return connectionFactory.createConnection(connectionData); + } + + private ConnectionData mapConnectionData(final SocialRedisConnection redisConnection) { + return new ConnectionData(redisConnection.getProviderId(), redisConnection.getProviderUserId(), redisConnection.getDisplayName(), redisConnection.getProfileUrl(), redisConnection.getImageUrl(), + decrypt(redisConnection.getAccessToken()), decrypt(redisConnection.getSecret()), decrypt(redisConnection.getRefreshToken()), redisConnection.getExpireTime()); + } + + private String decrypt(String encryptedText) { + return encryptedText == null ? null : textEncryptor.decrypt(encryptedText); + } + } + + private Connection findPrimaryConnection(String providerId) { + Iterable redisConnections = socialRedisConnectionRepository.findByUserIdAndProviderId(userId, providerId); + + List> primaryConnections = new ArrayList>(); + for (SocialRedisConnection connection : redisConnections) { + primaryConnections.add(connectionMapper.mapConnection(connection)); + } + + if (primaryConnections.size() > 0) { + return primaryConnections.get(0); + } else { + return null; + } + } + + private String getProviderId(Class apiType) { + return connectionFactoryLocator.getConnectionFactory(apiType).getProviderId(); + } + + private String encrypt(String text) { + return text == null ? null : textEncryptor.encrypt(text); + } +} diff --git a/spring-social-core/src/main/java/org/springframework/social/connect/redis/RedisUsersConnectionRepository.java b/spring-social-core/src/main/java/org/springframework/social/connect/redis/RedisUsersConnectionRepository.java new file mode 100644 index 000000000..67e90b9d8 --- /dev/null +++ b/spring-social-core/src/main/java/org/springframework/social/connect/redis/RedisUsersConnectionRepository.java @@ -0,0 +1,41 @@ +package org.springframework.social.connect.redis; + +import org.springframework.security.crypto.encrypt.TextEncryptor; +import org.springframework.social.connect.Connection; +import org.springframework.social.connect.ConnectionFactoryLocator; +import org.springframework.social.connect.ConnectionRepository; +import org.springframework.social.connect.UsersConnectionRepository; +import org.springframework.social.connect.redis.data.SocialRedisConnectionRepository; +import org.springframework.util.Assert; + +import java.util.List; +import java.util.Set; + +public class RedisUsersConnectionRepository implements UsersConnectionRepository { + + private final ConnectionFactoryLocator connectionFactoryLocator; + private final TextEncryptor textEncryptor; + private final SocialRedisConnectionRepository socialRedisConnectionRepository; + + public RedisUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator, TextEncryptor textEncryptor, SocialRedisConnectionRepository socialRedisConnectionRepository) { + Assert.notNull(connectionFactoryLocator); + Assert.notNull(textEncryptor); + Assert.notNull(socialRedisConnectionRepository); + + this.connectionFactoryLocator = connectionFactoryLocator; + this.textEncryptor = textEncryptor; + this.socialRedisConnectionRepository = socialRedisConnectionRepository; + } + + public List findUserIdsWithConnection(final Connection connection) { + return null; + } + + public Set findUserIdsConnectedTo(final String providerId, final Set providerUserIds) { + return null; + } + + public ConnectionRepository createConnectionRepository(final String userId) { + return null; + } +} diff --git a/spring-social-core/src/main/java/org/springframework/social/connect/redis/data/SocialRedisConnection.java b/spring-social-core/src/main/java/org/springframework/social/connect/redis/data/SocialRedisConnection.java new file mode 100644 index 000000000..752088aa9 --- /dev/null +++ b/spring-social-core/src/main/java/org/springframework/social/connect/redis/data/SocialRedisConnection.java @@ -0,0 +1,125 @@ +package org.springframework.social.connect.redis.data; + +import org.springframework.data.annotation.Id; +import org.springframework.data.redis.core.RedisHash; +import org.springframework.data.redis.core.index.Indexed; + +@RedisHash +public class SocialRedisConnection { + + @Id + private String providerUserId; + + @Indexed + private String userId; + + @Indexed + private String providerId; + + private String displayName; + + private String profileUrl; + + private String imageUrl; + + private String accessToken; + + private String secret; + + private String refreshToken; + + private Long expireTime; + + public SocialRedisConnection(String providerUserId, String userId, String providerId, String displayName, String profileUrl, String imageUrl, String accessToken, String secret, String refreshToken, Long expireTime) { + this.providerUserId = providerUserId; + this.userId = userId; + this.providerId = providerId; + this.displayName = displayName; + this.profileUrl = profileUrl; + this.imageUrl = imageUrl; + this.accessToken = accessToken; + this.secret = secret; + this.refreshToken = refreshToken; + this.expireTime = expireTime; + } + + public String getProviderUserId() { + return providerUserId; + } + + public String getUserId() { + return userId; + } + + public String getProviderId() { + return providerId; + } + + public String getDisplayName() { + return displayName; + } + + public String getProfileUrl() { + return profileUrl; + } + + public String getImageUrl() { + return imageUrl; + } + + public String getAccessToken() { + return accessToken; + } + + public String getSecret() { + return secret; + } + + public String getRefreshToken() { + return refreshToken; + } + + public Long getExpireTime() { + return expireTime; + } + + public void setProviderUserId(String providerUserId) { + this.providerUserId = providerUserId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public void setProviderId(String providerId) { + this.providerId = providerId; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + public void setProfileUrl(String profileUrl) { + this.profileUrl = profileUrl; + } + + public void setImageUrl(String imageUrl) { + this.imageUrl = imageUrl; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + public void setSecret(String secret) { + this.secret = secret; + } + + public void setRefreshToken(String refreshToken) { + this.refreshToken = refreshToken; + } + + public void setExpireTime(Long expireTime) { + this.expireTime = expireTime; + } +} diff --git a/spring-social-core/src/main/java/org/springframework/social/connect/redis/data/SocialRedisConnectionRepository.java b/spring-social-core/src/main/java/org/springframework/social/connect/redis/data/SocialRedisConnectionRepository.java new file mode 100644 index 000000000..37cfaafe5 --- /dev/null +++ b/spring-social-core/src/main/java/org/springframework/social/connect/redis/data/SocialRedisConnectionRepository.java @@ -0,0 +1,21 @@ +package org.springframework.social.connect.redis.data; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface SocialRedisConnectionRepository extends CrudRepository { + + Iterable findByProviderId(final String providerId); + + Iterable findByUserIdAndProviderId(final String userId, final String providerId); + + SocialRedisConnection findOneByUserIdAndProviderIdAndProviderUserId(String userId, String providerId, String providerUserId); + + void deleteByUserIdAndProviderId(final String userId, final String providerId); + + void deleteByUserIdAndProviderIdAndProviderUserId(final String userId, final String providerId, final String providerUserId); + + Iterable findByUserId(final String userId); + +} diff --git a/spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisAvailable.java b/spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisAvailable.java new file mode 100644 index 000000000..c1cc81cf3 --- /dev/null +++ b/spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisAvailable.java @@ -0,0 +1,12 @@ +package org.springframework.social.connect.redis; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +@interface RedisAvailable { + +} \ No newline at end of file diff --git a/spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisAvailableRule.java b/spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisAvailableRule.java new file mode 100644 index 000000000..4887c58f0 --- /dev/null +++ b/spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisAvailableRule.java @@ -0,0 +1,70 @@ +package org.springframework.social.connect.redis; + +import org.junit.Assume; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; +import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; + +class RedisAvailableRule implements TestRule { + + private static ThreadLocal connectionFactoryResource = new ThreadLocal<>(); + + private String redisHost; + + private int redisPort; + + RedisAvailableRule(final String redisHost, final int redisPort) { + this.redisHost = redisHost; + this.redisPort = redisPort; + } + + @Override + public Statement apply(final Statement base, Description description) { + RedisAvailable redisAvailable = description.getAnnotation(RedisAvailable.class); + if (redisAvailable != null) { + JedisConnectionFactory connectionFactory = null; + try { + connectionFactory = new JedisConnectionFactory(); + connectionFactory.setHostName(redisHost); + connectionFactory.setPort(redisPort); + connectionFactory.afterPropertiesSet(); + connectionFactory.getConnection(); + connectionFactoryResource.set(connectionFactory); + } catch (Exception e) { + if (connectionFactory != null) { + connectionFactory.destroy(); + } + return new Statement() { + @Override + public void evaluate() throws Throwable { + Assume.assumeTrue("Skipping test class: Redis not available at port " + redisHost + ":" + redisPort, false); + } + }; + } + + return new Statement() { + @Override + public void evaluate() throws Throwable { + try { + base.evaluate(); + } finally { + JedisConnectionFactory connectionFactory = connectionFactoryResource.get(); + connectionFactoryResource.remove(); + if (connectionFactory != null) { + connectionFactory.destroy(); + } + } + } + }; + } + + return new Statement() { + @Override + public void evaluate() throws Throwable { + base.evaluate(); + } + }; + + } +} diff --git a/spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisConnectionRepositoryIntegrationTests.java b/spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisConnectionRepositoryIntegrationTests.java new file mode 100644 index 000000000..4118a4a6d --- /dev/null +++ b/spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisConnectionRepositoryIntegrationTests.java @@ -0,0 +1,182 @@ +package org.springframework.social.connect.redis; + +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.FilterType; +import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; +import org.springframework.security.crypto.encrypt.Encryptors; +import org.springframework.social.connect.*; +import org.springframework.social.connect.redis.data.SocialRedisConnectionRepository; +import org.springframework.social.connect.support.AbstractConnection; +import org.springframework.social.connect.support.ConnectionFactoryRegistry; +import org.springframework.social.connect.support.OAuth1ConnectionFactory; +import org.springframework.social.oauth1.OAuth1Operations; +import org.springframework.social.oauth1.OAuth1ServiceProvider; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.util.MultiValueMap; +import redis.clients.jedis.JedisShardInfo; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +@RedisAvailable +public class RedisConnectionRepositoryIntegrationTests { + + private static final String REDIS_HOST = "127.0.0.1"; + private static final int REDIS_PORT = 6379; + + @ClassRule + public static TestRule redisAvailableRule = new RedisAvailableRule(REDIS_HOST, REDIS_PORT); + + @Autowired + @SuppressWarnings("SpringJavaAutowiringInspection") + private SocialRedisConnectionRepository socialRedisConnectionRepository; + + @Autowired + private ConnectionFactoryLocator connectionFactoryLocator; + + private RedisConnectionRepository redisConnectionRepository; + + private TestConnection testConnection; + + @Before + public void init() { + redisConnectionRepository = new RedisConnectionRepository(connectionFactoryLocator, Encryptors.noOpText(), socialRedisConnectionRepository, "Turbots"); + testConnection = new TestConnection(new TestTwitterApiAdapter()); + socialRedisConnectionRepository.deleteAll(); + } + + @Test + public void addConnection() { + redisConnectionRepository.addConnection(testConnection); + + MultiValueMap> map = redisConnectionRepository.findAllConnections(); + + assertEquals(1, map.get("twitter").size()); + } + + @Test + public void removeConnections() { + redisConnectionRepository.addConnection(testConnection); + + redisConnectionRepository.removeConnections("twitter"); + + MultiValueMap> map = redisConnectionRepository.findAllConnections(); + + assertEquals(0, map.get("twitter").size()); + } + + @Test + public void findPrimaryConnection() { + redisConnectionRepository.addConnection(testConnection); + + Connection connection = redisConnectionRepository.findPrimaryConnection(TestTwitterApi.class); + + assertNotNull(connection); + assertEquals(connection.getDisplayName(), "displayName"); + } + + @Configuration + @EnableRedisRepositories(includeFilters = {@ComponentScan.Filter(type = FilterType.REGEX, pattern = ".*SocialRedisConnectionRepository")}) + static class Config { + + @Bean + RedisTemplate redisTemplate() { + JedisConnectionFactory connectionFactory = new JedisConnectionFactory(new JedisShardInfo(REDIS_HOST, REDIS_PORT)); + connectionFactory.afterPropertiesSet(); + + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + + return template; + } + + @Bean + public ConnectionFactoryLocator connectionFactoryLocator() { + ConnectionFactoryRegistry registry = new ConnectionFactoryRegistry(); + registry.addConnectionFactory(new TestTwitterConnectionFactory()); + return registry; + } + } + + class TestConnection extends AbstractConnection { + + TestConnection(ApiAdapter apiAdapter) { + //noinspection unchecked + super(apiAdapter); + } + + @Override + public Object getApi() { + return "twitter"; + } + + @Override + public ConnectionData createData() { + return new ConnectionData("twitter", "providerUserId", "displayName", "profileUrl", "imageUrl", "accessToken", "secret", "refreshToken", 1000000L); + } + } + + private static class TestTwitterConnectionFactory extends OAuth1ConnectionFactory { + + public TestTwitterConnectionFactory() { + super("twitter", new TestTwitterServiceProvider(), new TestTwitterApiAdapter()); + } + + } + + private static class TestTwitterServiceProvider implements OAuth1ServiceProvider { + + public OAuth1Operations getOAuthOperations() { + return null; + } + + public TestTwitterApi getApi(final String accessToken, final String secret) { + return new TestTwitterApi() { + public String getAccessToken() { + return accessToken; + } + + }; + } + + } + + public interface TestTwitterApi { + + String getAccessToken(); + + } + + private static class TestTwitterApiAdapter implements ApiAdapter { + + private String name = "@dhubau"; + + public boolean test(TestTwitterApi api) { + return true; + } + + public void setConnectionValues(TestTwitterApi api, ConnectionValues values) { + } + + public UserProfile fetchUserProfile(TestTwitterApi api) { + return new UserProfileBuilder().setName(name).setUsername(name).build(); + } + + public void updateStatus(TestTwitterApi api, String message) { + } + + } +} From d99e44e4bd84addaa2d36aad8c1a02793d099422 Mon Sep 17 00:00:00 2001 From: Dieter Hubau Date: Thu, 31 Mar 2016 17:54:06 +0200 Subject: [PATCH 2/3] Updated PR with improvements Added unit tests running on local redis Unit tests will be skipped if redis is not found through the use of JUnit rules Fixed a bug when finding social connections by providerUserId - field was not Indexed in Redis Implemented and tested RedisUsersConnectionRepository --- .../redis/RedisUsersConnectionRepository.java | 27 +++- .../redis/data/SocialRedisConnection.java | 1 + .../data/SocialRedisConnectionRepository.java | 3 +- ...sConnectionRepositoryIntegrationTests.java | 100 ++++----------- ...sConnectionRepositoryIntegrationTests.java | 117 ++++++++++++++++++ .../social/connect/redis/RedisUtils.java | 87 +++++++++++++ 6 files changed, 252 insertions(+), 83 deletions(-) create mode 100644 spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisUsersConnectionRepositoryIntegrationTests.java create mode 100644 spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisUtils.java diff --git a/spring-social-core/src/main/java/org/springframework/social/connect/redis/RedisUsersConnectionRepository.java b/spring-social-core/src/main/java/org/springframework/social/connect/redis/RedisUsersConnectionRepository.java index 67e90b9d8..dbae3d1ae 100644 --- a/spring-social-core/src/main/java/org/springframework/social/connect/redis/RedisUsersConnectionRepository.java +++ b/spring-social-core/src/main/java/org/springframework/social/connect/redis/RedisUsersConnectionRepository.java @@ -5,9 +5,12 @@ import org.springframework.social.connect.ConnectionFactoryLocator; import org.springframework.social.connect.ConnectionRepository; import org.springframework.social.connect.UsersConnectionRepository; +import org.springframework.social.connect.redis.data.SocialRedisConnection; import org.springframework.social.connect.redis.data.SocialRedisConnectionRepository; import org.springframework.util.Assert; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Set; @@ -28,14 +31,32 @@ public RedisUsersConnectionRepository(ConnectionFactoryLocator connectionFactory } public List findUserIdsWithConnection(final Connection connection) { - return null; + String providerId = connection.getKey().getProviderId(); + String providerUserId = connection.getKey().getProviderUserId(); + + Iterable connections = socialRedisConnectionRepository.findByProviderIdAndProviderUserId(providerId, providerUserId); + + List userIds = new ArrayList(); + for (SocialRedisConnection socialRedisConnection : connections) { + userIds.add(socialRedisConnection.getUserId()); + } + + return userIds; } public Set findUserIdsConnectedTo(final String providerId, final Set providerUserIds) { - return null; + Set userIds = new HashSet(); + + for (String providerUserId : providerUserIds) { + for (SocialRedisConnection socialRedisConnection : socialRedisConnectionRepository.findByProviderIdAndProviderUserId(providerId, providerUserId)) { + userIds.add(socialRedisConnection.getUserId()); + } + } + + return userIds; } public ConnectionRepository createConnectionRepository(final String userId) { - return null; + return new RedisConnectionRepository(connectionFactoryLocator, textEncryptor, socialRedisConnectionRepository, userId); } } diff --git a/spring-social-core/src/main/java/org/springframework/social/connect/redis/data/SocialRedisConnection.java b/spring-social-core/src/main/java/org/springframework/social/connect/redis/data/SocialRedisConnection.java index 752088aa9..69a3bb56f 100644 --- a/spring-social-core/src/main/java/org/springframework/social/connect/redis/data/SocialRedisConnection.java +++ b/spring-social-core/src/main/java/org/springframework/social/connect/redis/data/SocialRedisConnection.java @@ -8,6 +8,7 @@ public class SocialRedisConnection { @Id + @Indexed private String providerUserId; @Indexed diff --git a/spring-social-core/src/main/java/org/springframework/social/connect/redis/data/SocialRedisConnectionRepository.java b/spring-social-core/src/main/java/org/springframework/social/connect/redis/data/SocialRedisConnectionRepository.java index 37cfaafe5..0dfc393d1 100644 --- a/spring-social-core/src/main/java/org/springframework/social/connect/redis/data/SocialRedisConnectionRepository.java +++ b/spring-social-core/src/main/java/org/springframework/social/connect/redis/data/SocialRedisConnectionRepository.java @@ -12,10 +12,9 @@ public interface SocialRedisConnectionRepository extends CrudRepository findByUserId(final String userId); + Iterable findByProviderIdAndProviderUserId(final String providerId, final String providerUserId); } diff --git a/spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisConnectionRepositoryIntegrationTests.java b/spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisConnectionRepositoryIntegrationTests.java index 4118a4a6d..558752afd 100644 --- a/spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisConnectionRepositoryIntegrationTests.java +++ b/spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisConnectionRepositoryIntegrationTests.java @@ -14,13 +14,11 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; import org.springframework.security.crypto.encrypt.Encryptors; -import org.springframework.social.connect.*; +import org.springframework.social.connect.Connection; +import org.springframework.social.connect.ConnectionFactoryLocator; +import org.springframework.social.connect.NotConnectedException; import org.springframework.social.connect.redis.data.SocialRedisConnectionRepository; -import org.springframework.social.connect.support.AbstractConnection; import org.springframework.social.connect.support.ConnectionFactoryRegistry; -import org.springframework.social.connect.support.OAuth1ConnectionFactory; -import org.springframework.social.oauth1.OAuth1Operations; -import org.springframework.social.oauth1.OAuth1ServiceProvider; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.util.MultiValueMap; @@ -49,12 +47,12 @@ public class RedisConnectionRepositoryIntegrationTests { private RedisConnectionRepository redisConnectionRepository; - private TestConnection testConnection; + private RedisUtils.TestConnection testConnection; @Before public void init() { redisConnectionRepository = new RedisConnectionRepository(connectionFactoryLocator, Encryptors.noOpText(), socialRedisConnectionRepository, "Turbots"); - testConnection = new TestConnection(new TestTwitterApiAdapter()); + testConnection = new RedisUtils.TestConnection(new RedisUtils.TestTwitterApiAdapter(), "twitter:dhubau"); socialRedisConnectionRepository.deleteAll(); } @@ -78,11 +76,26 @@ public void removeConnections() { assertEquals(0, map.get("twitter").size()); } + @Test + public void getPrimaryConnectionOK() { + redisConnectionRepository.addConnection(testConnection); + + Connection connection = redisConnectionRepository.getPrimaryConnection(RedisUtils.TestTwitterApi.class); + + assertNotNull(connection); + assertEquals(connection.getDisplayName(), "displayName"); + } + + @Test(expected = NotConnectedException.class) + public void getPrimaryConnectionException() throws NotConnectedException { + redisConnectionRepository.getPrimaryConnection(RedisUtils.TestTwitterApi.class); + } + @Test public void findPrimaryConnection() { redisConnectionRepository.addConnection(testConnection); - Connection connection = redisConnectionRepository.findPrimaryConnection(TestTwitterApi.class); + Connection connection = redisConnectionRepository.findPrimaryConnection(RedisUtils.TestTwitterApi.class); assertNotNull(connection); assertEquals(connection.getDisplayName(), "displayName"); @@ -106,77 +119,8 @@ static class Config { @Bean public ConnectionFactoryLocator connectionFactoryLocator() { ConnectionFactoryRegistry registry = new ConnectionFactoryRegistry(); - registry.addConnectionFactory(new TestTwitterConnectionFactory()); + registry.addConnectionFactory(new RedisUtils.TestTwitterConnectionFactory()); return registry; } } - - class TestConnection extends AbstractConnection { - - TestConnection(ApiAdapter apiAdapter) { - //noinspection unchecked - super(apiAdapter); - } - - @Override - public Object getApi() { - return "twitter"; - } - - @Override - public ConnectionData createData() { - return new ConnectionData("twitter", "providerUserId", "displayName", "profileUrl", "imageUrl", "accessToken", "secret", "refreshToken", 1000000L); - } - } - - private static class TestTwitterConnectionFactory extends OAuth1ConnectionFactory { - - public TestTwitterConnectionFactory() { - super("twitter", new TestTwitterServiceProvider(), new TestTwitterApiAdapter()); - } - - } - - private static class TestTwitterServiceProvider implements OAuth1ServiceProvider { - - public OAuth1Operations getOAuthOperations() { - return null; - } - - public TestTwitterApi getApi(final String accessToken, final String secret) { - return new TestTwitterApi() { - public String getAccessToken() { - return accessToken; - } - - }; - } - - } - - public interface TestTwitterApi { - - String getAccessToken(); - - } - - private static class TestTwitterApiAdapter implements ApiAdapter { - - private String name = "@dhubau"; - - public boolean test(TestTwitterApi api) { - return true; - } - - public void setConnectionValues(TestTwitterApi api, ConnectionValues values) { - } - - public UserProfile fetchUserProfile(TestTwitterApi api) { - return new UserProfileBuilder().setName(name).setUsername(name).build(); - } - - public void updateStatus(TestTwitterApi api, String message) { - } - - } } diff --git a/spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisUsersConnectionRepositoryIntegrationTests.java b/spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisUsersConnectionRepositoryIntegrationTests.java new file mode 100644 index 000000000..6c2fa73d4 --- /dev/null +++ b/spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisUsersConnectionRepositoryIntegrationTests.java @@ -0,0 +1,117 @@ +package org.springframework.social.connect.redis; + +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.FilterType; +import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; +import org.springframework.security.crypto.encrypt.Encryptors; +import org.springframework.social.connect.ConnectionFactoryLocator; +import org.springframework.social.connect.ConnectionRepository; +import org.springframework.social.connect.redis.data.SocialRedisConnectionRepository; +import org.springframework.social.connect.support.ConnectionFactoryRegistry; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import redis.clients.jedis.JedisShardInfo; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.junit.Assert.assertEquals; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration +@RedisAvailable +public class RedisUsersConnectionRepositoryIntegrationTests { + + private static final String REDIS_HOST = "127.0.0.1"; + private static final int REDIS_PORT = 6379; + + @Autowired + @SuppressWarnings("SpringJavaAutowiringInspection") + private SocialRedisConnectionRepository socialRedisConnectionRepository; + + @ClassRule + public static TestRule redisAvailableRule = new RedisAvailableRule(REDIS_HOST, REDIS_PORT); + + @Autowired + private ConnectionFactoryLocator connectionFactoryLocator; + + private RedisUsersConnectionRepository redisUsersConnectionRepository; + + private RedisUtils.TestConnection connectionForDhubau; + private RedisUtils.TestConnection connectionForMyBuddy; + + @Before + public void init() { + redisUsersConnectionRepository = new RedisUsersConnectionRepository(connectionFactoryLocator, Encryptors.noOpText(), socialRedisConnectionRepository); + connectionForDhubau = new RedisUtils.TestConnection(new RedisUtils.TestTwitterApiAdapter(), "twitter:dhubau"); + connectionForMyBuddy = new RedisUtils.TestConnection(new RedisUtils.TestTwitterApiAdapter(), "twitter:buddy"); + socialRedisConnectionRepository.deleteAll(); + } + + @Test + public void findUserIdsWithConnectionNoUsers() { + List userIds = redisUsersConnectionRepository.findUserIdsWithConnection(connectionForDhubau); + + assertEquals(0, userIds.size()); + } + + @Test + public void findUserIdsWithConnectionOneUser() { + ConnectionRepository connectionRepository = redisUsersConnectionRepository.createConnectionRepository("dhubau"); + connectionRepository.addConnection(connectionForDhubau); + connectionRepository.addConnection(connectionForMyBuddy); + + List userIds = redisUsersConnectionRepository.findUserIdsWithConnection(connectionForDhubau); + + assertEquals(1, userIds.size()); + } + + @Test + public void findUserIdsConnectedToWithTwoUsers() { + ConnectionRepository connectionRepository = redisUsersConnectionRepository.createConnectionRepository("dhubau"); + connectionRepository.addConnection(connectionForDhubau); + connectionRepository = redisUsersConnectionRepository.createConnectionRepository("buddy"); + connectionRepository.addConnection(connectionForMyBuddy); + + Set providerUserIds = new HashSet<>(); + Collections.addAll(providerUserIds, "twitter:dhubau", "twitter:buddy"); + Set userIds = redisUsersConnectionRepository.findUserIdsConnectedTo("twitter", providerUserIds); + + assertEquals(2, userIds.size()); + } + + @Configuration + @EnableRedisRepositories(includeFilters = {@ComponentScan.Filter(type = FilterType.REGEX, pattern = ".*SocialRedisConnectionRepository")}) + static class Config { + + @Bean + RedisTemplate redisTemplate() { + JedisConnectionFactory connectionFactory = new JedisConnectionFactory(new JedisShardInfo(REDIS_HOST, REDIS_PORT)); + connectionFactory.afterPropertiesSet(); + + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + + return template; + } + + @Bean + public ConnectionFactoryLocator connectionFactoryLocator() { + ConnectionFactoryRegistry registry = new ConnectionFactoryRegistry(); + registry.addConnectionFactory(new RedisUtils.TestTwitterConnectionFactory()); + return registry; + } + } +} diff --git a/spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisUtils.java b/spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisUtils.java new file mode 100644 index 000000000..03edfc8b4 --- /dev/null +++ b/spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisUtils.java @@ -0,0 +1,87 @@ +package org.springframework.social.connect.redis; + +import org.springframework.social.connect.*; +import org.springframework.social.connect.support.AbstractConnection; +import org.springframework.social.connect.support.OAuth1ConnectionFactory; +import org.springframework.social.oauth1.OAuth1Operations; +import org.springframework.social.oauth1.OAuth1ServiceProvider; + +class RedisUtils { + + static class TestConnection extends AbstractConnection { + + private String providerUserId; + + TestConnection(final ApiAdapter apiAdapter, final String providerUserId) { + //noinspection unchecked + super(apiAdapter); + this.providerUserId = providerUserId; + } + + @Override + public Object getApi() { + return "twitter"; + } + + @Override + public ConnectionKey getKey() { + return new ConnectionKey("twitter", providerUserId); + } + + @Override + public ConnectionData createData() { + return new ConnectionData("twitter", providerUserId, "displayName", "profileUrl", "imageUrl", "accessToken", "secret", "refreshToken", 1000000L); + } + } + + static class TestTwitterConnectionFactory extends OAuth1ConnectionFactory { + + TestTwitterConnectionFactory() { + super("twitter", new TestTwitterServiceProvider(), new TestTwitterApiAdapter()); + } + + } + + private static class TestTwitterServiceProvider implements OAuth1ServiceProvider { + + public OAuth1Operations getOAuthOperations() { + return null; + } + + public TestTwitterApi getApi(final String accessToken, final String secret) { + return new TestTwitterApi() { + public String getAccessToken() { + return accessToken; + } + + }; + } + + } + + interface TestTwitterApi { + + String getAccessToken(); + + } + + static class TestTwitterApiAdapter implements ApiAdapter { + + private String name = "@dhubau"; + + public boolean test(TestTwitterApi api) { + return true; + } + + public void setConnectionValues(TestTwitterApi api, ConnectionValues values) { + } + + public UserProfile fetchUserProfile(TestTwitterApi api) { + return new UserProfileBuilder().setName(name).setUsername(name).build(); + } + + public void updateStatus(TestTwitterApi api, String message) { + } + + } +} From d194872ffa0510f9bdef44c7a5ec4c76bb7778cb Mon Sep 17 00:00:00 2001 From: Dieter Hubau Date: Fri, 27 May 2016 22:10:26 +0200 Subject: [PATCH 3/3] Rework after comments and added unit tests Updated spring-data-redis version to 1.7.1.RELEASE Added RedisHash keyspace for server side queries in case of Redis Cluster Updated delete method Added unit tests --- gradle.properties | 2 +- gradle/wrapper/gradle-wrapper.properties | 4 +- .../redis/RedisConnectionRepository.java | 10 ++++- .../redis/data/SocialRedisConnection.java | 2 +- .../data/SocialRedisConnectionRepository.java | 1 + ...sConnectionRepositoryIntegrationTests.java | 45 ++++++++++++++++--- .../social/connect/redis/RedisUtils.java | 8 +++- 7 files changed, 60 insertions(+), 12 deletions(-) diff --git a/gradle.properties b/gradle.properties index ac1637d8a..90f7998c8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ h2Version=1.3.176 springSecurityVersion=3.2.9.RELEASE -springDataRedisVersion=1.7.0.RC1 +springDataRedisVersion=1.7.1.RELEASE apacheCommonsPoolVersion=2.2 junitVersion=4.12 httpComponentsVersion=4.3.6 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a6cc5b424..e1fc8a2be 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Jun 30 12:43:52 CDT 2014 +#Fri May 27 21:26:51 CEST 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-bin.zip +distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip diff --git a/spring-social-core/src/main/java/org/springframework/social/connect/redis/RedisConnectionRepository.java b/spring-social-core/src/main/java/org/springframework/social/connect/redis/RedisConnectionRepository.java index edeb7a18b..6da91c16b 100644 --- a/spring-social-core/src/main/java/org/springframework/social/connect/redis/RedisConnectionRepository.java +++ b/spring-social-core/src/main/java/org/springframework/social/connect/redis/RedisConnectionRepository.java @@ -127,7 +127,12 @@ public void removeConnections(String providerId) { } public void removeConnection(ConnectionKey connectionKey) { - socialRedisConnectionRepository.deleteByUserIdAndProviderIdAndProviderUserId(userId, connectionKey.getProviderId(), connectionKey.getProviderUserId()); + // TODO: Wait for DATAKV-135 in order to use this: + // socialRedisConnectionRepository.deleteByUserIdAndProviderIdAndProviderUserId + // (userId, connectionKey.getProviderId(), connectionKey.getProviderUserId()); + + SocialRedisConnection socialRedisConnection = socialRedisConnectionRepository.findOneByUserIdAndProviderIdAndProviderUserId(userId, connectionKey.getProviderId(), connectionKey.getProviderUserId()); + socialRedisConnectionRepository.delete(socialRedisConnection); } private final RedisConnectionMapper connectionMapper = new RedisConnectionMapper(); @@ -135,6 +140,9 @@ public void removeConnection(ConnectionKey connectionKey) { private final class RedisConnectionMapper { Connection mapConnection(final SocialRedisConnection redisConnection) { + if (redisConnection == null) { + throw new EmptyResultDataAccessException(1); + } ConnectionData connectionData = mapConnectionData(redisConnection); ConnectionFactory connectionFactory = connectionFactoryLocator.getConnectionFactory(connectionData.getProviderId()); return connectionFactory.createConnection(connectionData); diff --git a/spring-social-core/src/main/java/org/springframework/social/connect/redis/data/SocialRedisConnection.java b/spring-social-core/src/main/java/org/springframework/social/connect/redis/data/SocialRedisConnection.java index 69a3bb56f..984c1c8a0 100644 --- a/spring-social-core/src/main/java/org/springframework/social/connect/redis/data/SocialRedisConnection.java +++ b/spring-social-core/src/main/java/org/springframework/social/connect/redis/data/SocialRedisConnection.java @@ -4,7 +4,7 @@ import org.springframework.data.redis.core.RedisHash; import org.springframework.data.redis.core.index.Indexed; -@RedisHash +@RedisHash("socialRedisConnection") public class SocialRedisConnection { @Id diff --git a/spring-social-core/src/main/java/org/springframework/social/connect/redis/data/SocialRedisConnectionRepository.java b/spring-social-core/src/main/java/org/springframework/social/connect/redis/data/SocialRedisConnectionRepository.java index 0dfc393d1..5ab0219ea 100644 --- a/spring-social-core/src/main/java/org/springframework/social/connect/redis/data/SocialRedisConnectionRepository.java +++ b/spring-social-core/src/main/java/org/springframework/social/connect/redis/data/SocialRedisConnectionRepository.java @@ -12,6 +12,7 @@ public interface SocialRedisConnectionRepository extends CrudRepository findByUserId(final String userId); diff --git a/spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisConnectionRepositoryIntegrationTests.java b/spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisConnectionRepositoryIntegrationTests.java index 558752afd..d360dae50 100644 --- a/spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisConnectionRepositoryIntegrationTests.java +++ b/spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisConnectionRepositoryIntegrationTests.java @@ -14,9 +14,7 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; import org.springframework.security.crypto.encrypt.Encryptors; -import org.springframework.social.connect.Connection; -import org.springframework.social.connect.ConnectionFactoryLocator; -import org.springframework.social.connect.NotConnectedException; +import org.springframework.social.connect.*; import org.springframework.social.connect.redis.data.SocialRedisConnectionRepository; import org.springframework.social.connect.support.ConnectionFactoryRegistry; import org.springframework.test.context.ContextConfiguration; @@ -24,8 +22,7 @@ import org.springframework.util.MultiValueMap; import redis.clients.jedis.JedisShardInfo; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.*; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration @@ -76,6 +73,44 @@ public void removeConnections() { assertEquals(0, map.get("twitter").size()); } + @Test + public void removeConnection() { + redisConnectionRepository.addConnection(testConnection); + + redisConnectionRepository.removeConnection(testConnection.getKey()); + + MultiValueMap> map = redisConnectionRepository.findAllConnections(); + + assertEquals(0, map.get("twitter").size()); + } + + @Test + public void getConnectionWhenFound() { + redisConnectionRepository.addConnection(testConnection); + + Connection socialRedisConnection = redisConnectionRepository.getConnection(testConnection.getKey()); + assertNotNull(socialRedisConnection); + assertEquals(testConnection.getKey().getProviderUserId(), socialRedisConnection.getKey().getProviderUserId()); + } + + @Test(expected = NoSuchConnectionException.class) + public void getConnectionWhenNotFound() { + ConnectionKey connectionKey = new ConnectionKey("foo", "bar"); + redisConnectionRepository.getConnection(connectionKey); + } + + @Test + public void updateConnection() { + assertNull(testConnection.getDisplayName()); + redisConnectionRepository.addConnection(testConnection); + + redisConnectionRepository.updateConnection(testConnection); + + Connection socialRedisConnection = redisConnectionRepository.getConnection(testConnection.getKey()); + assertNotNull(socialRedisConnection); + assertEquals("displayName", socialRedisConnection.getDisplayName()); + } + @Test public void getPrimaryConnectionOK() { redisConnectionRepository.addConnection(testConnection); diff --git a/spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisUtils.java b/spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisUtils.java index 03edfc8b4..95e70eec9 100644 --- a/spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisUtils.java +++ b/spring-social-core/src/test/java/org/springframework/social/connect/redis/RedisUtils.java @@ -13,14 +13,18 @@ static class TestConnection extends AbstractConnection { private String providerUserId; TestConnection(final ApiAdapter apiAdapter, final String providerUserId) { - //noinspection unchecked super(apiAdapter); this.providerUserId = providerUserId; } @Override public Object getApi() { - return "twitter"; + return new TestTwitterApi() { + @Override + public String getAccessToken() { + return "accessToken-123"; + } + }; } @Override