From e5e882cca22db32a76e0a6f33d59c29443a55ead Mon Sep 17 00:00:00 2001 From: Stav Shamir Date: Mon, 6 Aug 2018 15:23:43 +0300 Subject: [PATCH] Initial commit --- build.gradle | 42 +++++++++++ settings.gradle | 2 + .../com/stavshamir/app/AppController.java | 14 ++++ .../java/com/stavshamir/app/Application.java | 16 ++++ .../app/authorization/AuthTokens.java | 46 ++++++++++++ .../authorization/AuthTokensController.java | 45 ++++++++++++ .../authorization/AuthTokensRepository.java | 9 +++ .../app/authorization/AuthTokensService.java | 13 ++++ .../authorization/AuthTokensServiceImpl.java | 73 +++++++++++++++++++ .../stavshamir/app/spotify/SpotifyClient.java | 30 ++++++++ src/main/resources/application.properties | 11 +++ 11 files changed, 301 insertions(+) create mode 100644 build.gradle create mode 100644 settings.gradle create mode 100644 src/main/java/com/stavshamir/app/AppController.java create mode 100644 src/main/java/com/stavshamir/app/Application.java create mode 100644 src/main/java/com/stavshamir/app/authorization/AuthTokens.java create mode 100644 src/main/java/com/stavshamir/app/authorization/AuthTokensController.java create mode 100644 src/main/java/com/stavshamir/app/authorization/AuthTokensRepository.java create mode 100644 src/main/java/com/stavshamir/app/authorization/AuthTokensService.java create mode 100644 src/main/java/com/stavshamir/app/authorization/AuthTokensServiceImpl.java create mode 100644 src/main/java/com/stavshamir/app/spotify/SpotifyClient.java create mode 100644 src/main/resources/application.properties diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..3211493 --- /dev/null +++ b/build.gradle @@ -0,0 +1,42 @@ +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath("org.springframework.boot:spring-boot-gradle-plugin:2.0.2.RELEASE") + } +} + +group 'listening-history' +version '1.0-SNAPSHOT' + +apply plugin: 'java' +apply plugin: 'eclipse' +apply plugin: 'idea' +apply plugin: 'org.springframework.boot' +apply plugin: 'io.spring.dependency-management' + +bootJar { + baseName = 'gs-spring-boot' + version = '0.1.0' +} + +repositories { + mavenCentral() +} + +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + +dependencies { + compile 'org.springframework.boot:spring-boot-starter-web' + compile 'org.springframework.boot:spring-boot-starter-data-jpa' + + compile group: 'org.postgresql', name: 'postgresql' + + compile 'se.michaelthelin.spotify:spotify-web-api-java:2.0.2' + + testCompile 'org.springframework.boot:spring-boot-starter-test' + testCompile 'com.h2database:h2' + testCompile 'junit:junit' +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..8980d16 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'spotify-listening-history' + diff --git a/src/main/java/com/stavshamir/app/AppController.java b/src/main/java/com/stavshamir/app/AppController.java new file mode 100644 index 0000000..a00421a --- /dev/null +++ b/src/main/java/com/stavshamir/app/AppController.java @@ -0,0 +1,14 @@ +package com.stavshamir.app; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class AppController { + + @RequestMapping("/health") + public String health() { + return "healthy"; + } + +} diff --git a/src/main/java/com/stavshamir/app/Application.java b/src/main/java/com/stavshamir/app/Application.java new file mode 100644 index 0000000..fcb0432 --- /dev/null +++ b/src/main/java/com/stavshamir/app/Application.java @@ -0,0 +1,16 @@ +package com.stavshamir.app; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} + diff --git a/src/main/java/com/stavshamir/app/authorization/AuthTokens.java b/src/main/java/com/stavshamir/app/authorization/AuthTokens.java new file mode 100644 index 0000000..53b03e8 --- /dev/null +++ b/src/main/java/com/stavshamir/app/authorization/AuthTokens.java @@ -0,0 +1,46 @@ +package com.stavshamir.app.authorization; + +import javax.persistence.Entity; +import javax.persistence.Id; + +@Entity +public class AuthTokens { + + @Id + private String userId; + + private String accessToken; + private String refreshToken; + + public AuthTokens() {} + + public AuthTokens(String userId, String accessToken, String refreshToken) { + this.userId = userId; + this.accessToken = accessToken; + this.refreshToken = refreshToken; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + public String getRefreshToken() { + return refreshToken; + } + + public void setRefreshToken(String refreshToken) { + this.refreshToken = refreshToken; + } +} diff --git a/src/main/java/com/stavshamir/app/authorization/AuthTokensController.java b/src/main/java/com/stavshamir/app/authorization/AuthTokensController.java new file mode 100644 index 0000000..60de75e --- /dev/null +++ b/src/main/java/com/stavshamir/app/authorization/AuthTokensController.java @@ -0,0 +1,45 @@ +package com.stavshamir.app.authorization; + +import com.wrapper.spotify.exceptions.SpotifyWebApiException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.view.RedirectView; + +import java.io.IOException; + +@RestController +public class AuthTokensController { + + private final AuthTokensService authTokensService; + + @Autowired + public AuthTokensController(AuthTokensService authTokensService) { + this.authTokensService = authTokensService; + } + + @RequestMapping("/authorize") + public RedirectView authorize() { + String url = authTokensService + .getAuthorizationCodeUriRequest("user-read-recently-played") + .execute() + .toString(); + + RedirectView redirectView = new RedirectView(); + redirectView.setUrl(url); + return redirectView; + } + + @RequestMapping("/callback") + public String callback(@RequestParam("code") String code) { + try { + authTokensService.retrieveAndPersistTokens(code); + } catch (IOException | SpotifyWebApiException e) { + return "Failed to retrieve authorization credentials from Spotify API: " + e.getMessage(); + } + + return "Authorization credentials stored successfully"; + } + +} diff --git a/src/main/java/com/stavshamir/app/authorization/AuthTokensRepository.java b/src/main/java/com/stavshamir/app/authorization/AuthTokensRepository.java new file mode 100644 index 0000000..922d148 --- /dev/null +++ b/src/main/java/com/stavshamir/app/authorization/AuthTokensRepository.java @@ -0,0 +1,9 @@ +package com.stavshamir.app.authorization; + +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface AuthTokensRepository extends JpaRepository { + Optional findByUserId(String userId); +} diff --git a/src/main/java/com/stavshamir/app/authorization/AuthTokensService.java b/src/main/java/com/stavshamir/app/authorization/AuthTokensService.java new file mode 100644 index 0000000..6b4dfc5 --- /dev/null +++ b/src/main/java/com/stavshamir/app/authorization/AuthTokensService.java @@ -0,0 +1,13 @@ +package com.stavshamir.app.authorization; + +import com.wrapper.spotify.exceptions.SpotifyWebApiException; +import com.wrapper.spotify.requests.authorization.authorization_code.AuthorizationCodeUriRequest; + +import java.io.IOException; + +public interface AuthTokensService { + + AuthorizationCodeUriRequest getAuthorizationCodeUriRequest(String scope); + void retrieveAndPersistTokens(String code) throws IOException, SpotifyWebApiException; + +} diff --git a/src/main/java/com/stavshamir/app/authorization/AuthTokensServiceImpl.java b/src/main/java/com/stavshamir/app/authorization/AuthTokensServiceImpl.java new file mode 100644 index 0000000..572ad6f --- /dev/null +++ b/src/main/java/com/stavshamir/app/authorization/AuthTokensServiceImpl.java @@ -0,0 +1,73 @@ +package com.stavshamir.app.authorization; + +import com.stavshamir.app.spotify.SpotifyClient; +import com.wrapper.spotify.exceptions.SpotifyWebApiException; +import com.wrapper.spotify.model_objects.credentials.AuthorizationCodeCredentials; +import com.wrapper.spotify.requests.authorization.authorization_code.AuthorizationCodeRequest; +import com.wrapper.spotify.requests.authorization.authorization_code.AuthorizationCodeUriRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.IOException; + +@Service +public class AuthTokensServiceImpl implements AuthTokensService { + + private Logger logger = LoggerFactory.getLogger(AuthTokensServiceImpl.class); + + private final SpotifyClient spotifyClient; + private final AuthTokensRepository authTokensRepository; + + @Autowired + public AuthTokensServiceImpl(SpotifyClient spotifyClient, AuthTokensRepository authTokensRepository) { + this.spotifyClient = spotifyClient; + this.authTokensRepository = authTokensRepository; + } + + @Override + public AuthorizationCodeUriRequest getAuthorizationCodeUriRequest(String scope) { + return spotifyClient + .getSpotifyApi() + .authorizationCodeUri() + .scope(scope) + .build(); + } + + @Override + public void retrieveAndPersistTokens(String code) throws IOException, SpotifyWebApiException { + AuthorizationCodeCredentials credentials = getAuthorizationCodeRequest(code).execute(); + + String userId = spotifyClient.getSpotifyApiWithAccessToken(credentials.getAccessToken()) + .getCurrentUsersProfile() + .build() + .execute() + .getUri(); + + persistTokens(userId, credentials); + } + + private AuthorizationCodeRequest getAuthorizationCodeRequest(String code) { + return spotifyClient + .getSpotifyApi() + .authorizationCode(code) + .build(); + } + + private void persistTokens(String userId, AuthorizationCodeCredentials credentials) { + String accessToken = credentials.getAccessToken(); + String refreshToken = credentials.getRefreshToken(); + + insertOrUpdateIfExists(userId, accessToken, refreshToken); + } + + private void insertOrUpdateIfExists(String userId, String accessToken, String refreshToken) { + AuthTokens auth = authTokensRepository + .findByUserId(userId) + .orElse(new AuthTokens(userId, accessToken, refreshToken)); + + authTokensRepository.save(auth); + } + +} diff --git a/src/main/java/com/stavshamir/app/spotify/SpotifyClient.java b/src/main/java/com/stavshamir/app/spotify/SpotifyClient.java new file mode 100644 index 0000000..b1da96d --- /dev/null +++ b/src/main/java/com/stavshamir/app/spotify/SpotifyClient.java @@ -0,0 +1,30 @@ +package com.stavshamir.app.spotify; + +import com.wrapper.spotify.SpotifyApi; +import com.wrapper.spotify.SpotifyHttpManager; +import org.springframework.stereotype.Component; + +import java.net.URI; + +@Component +public class SpotifyClient { + private final String CLIENT_ID = "94002a67e5704e2294bd4a5874cddec6"; + private final String CLIENT_SECRET = "f57133b0d830479ca8e17e4c9b5bf76d"; + private final URI REDIRECT_URI = SpotifyHttpManager.makeUri("http://localhost:5000/callback"); +// private final URI REDIRECT_URI = SpotifyHttpManager.makeUri("https://guarded-bayou-27287.herokuapp.com/callback"); + + private final SpotifyApi spotifyApi = new SpotifyApi.Builder() + .setClientId(CLIENT_ID) + .setClientSecret(CLIENT_SECRET) + .setRedirectUri(REDIRECT_URI) + .build(); + + public SpotifyApi getSpotifyApi() { + return spotifyApi; + } + + public SpotifyApi getSpotifyApiWithAccessToken(String accessToken) { + spotifyApi.setAccessToken(accessToken); + return spotifyApi; + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..dd66cf9 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,11 @@ +server.port = 5000 + +spring.datasource.driverClassName=org.postgresql.Driver +spring.datasource.platform=postgres + +spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQL94Dialect +spring.jpa.hibernate.ddl-auto=update + +spring.datasource.url=jdbc:postgresql://localhost:5432/spotify +spring.datasource.username=stav +spring.datasource.password=root \ No newline at end of file