Skip to content

Commit ffabe7d

Browse files
committed
Merge branch 'develop'
This merges the 1.3.0 changes
2 parents 4843af7 + 7afab24 commit ffabe7d

File tree

132 files changed

+2698
-1302
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

132 files changed

+2698
-1302
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ hs_err_pid*
7070
# Gradle:
7171
.idea/**/gradle.xml
7272
.idea/**/libraries
73+
.gradle
7374

7475
# Mongo Explorer plugin:
7576
.idea/**/mongoSettings.xml

backend/build.gradle

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ buildscript {
1919
}
2020

2121
plugins {
22-
id("org.ajoberstar.grgit") version "2.0.1"
22+
id("org.ajoberstar.grgit") version "2.1.0"
2323
id("net.researchgate.release") version "2.6.0"
2424
}
2525

@@ -43,27 +43,31 @@ repositories {
4343

4444
dependencies {
4545
compile("com.graphql-java:graphql-spring-boot-starter:4.0.0.M1")
46-
compile("com.graphql-java:graphiql-spring-boot-starter:3.10.0")
4746
compile("com.graphql-java:graphql-java-tools:4.3.0")
47+
compile("com.graphql-java:graphql-java:7.0")
4848
compile("org.springframework.boot:spring-boot-starter-web-services:$springBootVersion")
4949
compile("org.springframework.boot:spring-boot-starter-logging:$springBootVersion")
5050
compile("org.springframework.boot:spring-boot-starter-data-mongodb:$springBootVersion")
5151
compile("org.springframework.boot:spring-boot-starter-cache:$springBootVersion")
5252
compile("org.springframework.boot:spring-boot-starter-security:$springBootVersion")
5353
compile("org.springframework.security:spring-security-jwt:1.0.9.RELEASE")
5454
compile("org.springframework.security.oauth:spring-security-oauth2:2.2.1.RELEASE")
55-
compile("org.springframework.session:spring-session-data-mongodb:2.0.2.RELEASE")
56-
compile("org.springframework.session:spring-session-core:2.0.2.RELEASE")
55+
56+
/* The below is fix for kotlin-reflect in 1.2.20 */
57+
compile("org.springframework.data:spring-data-mongodb:2.0.3.RELEASE")
58+
compile("org.springframework.data:spring-data-commons:2.0.3.RELEASE")
59+
5760
compile("com.github.ben-manes.caffeine:caffeine:2.+")
58-
compile("org.jetbrains.kotlin:kotlin-stdlib-jre8:${kotlinVersion}")
59-
compile("org.jetbrains.kotlin:kotlin-stdlib-jre7:${kotlinVersion}")
60-
compile("org.jetbrains.kotlin:kotlin-stdlib:${kotlinVersion}")
61-
compile("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}")
62-
compile("com.fasterxml.jackson.module:jackson-module-kotlin:2.9.3")
63-
compile("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.3")
64-
compile("com.googlecode.libphonenumber:libphonenumber:8.4.2")
61+
compile("com.fasterxml.jackson.module:jackson-module-kotlin:2.9.4")
62+
compile("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.4")
63+
compile("com.googlecode.libphonenumber:libphonenumber:8.8.10")
6564
compile("com.google.guava:guava:23.6-jre")
6665
compile("net.sf.biweekly:biweekly:0.6.1")
66+
67+
compile("org.jetbrains.kotlin:kotlin-stdlib-jdk8:${kotlinVersion}")
68+
compile("org.jetbrains.kotlin:kotlin-stdlib:${kotlinVersion}")
69+
compile("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}")
70+
6771
testCompile("org.springframework.boot:spring-boot-starter-test:$springBootVersion")
6872
testCompile("org.junit.jupiter:junit-jupiter-api:${junitJupiterVersion}")
6973
testRuntime("org.junit.jupiter:junit-jupiter-engine:${junitJupiterVersion}")

backend/gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
version=1.2.5
1+
version=1.3.0

backend/src/main/kotlin/rocks/didit/sefilm/Application.kt

Lines changed: 56 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -16,37 +16,32 @@ import org.springframework.cache.annotation.EnableCaching
1616
import org.springframework.context.annotation.Bean
1717
import org.springframework.context.annotation.Primary
1818
import org.springframework.core.io.ClassPathResource
19-
import org.springframework.data.mongodb.core.convert.MongoCustomConversions
2019
import org.springframework.http.HttpEntity
2120
import org.springframework.http.HttpHeaders
2221
import org.springframework.http.MediaType
2322
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder
2423
import org.springframework.scheduling.annotation.EnableAsync
2524
import org.springframework.scheduling.annotation.EnableScheduling
2625
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
27-
import org.springframework.session.data.mongo.AbstractMongoSessionConverter
28-
import org.springframework.session.data.mongo.JdkMongoSessionConverter
29-
import org.springframework.session.data.mongo.config.annotation.web.http.EnableMongoHttpSession
3026
import org.springframework.web.client.RestTemplate
3127
import org.springframework.web.servlet.config.annotation.CorsRegistry
3228
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
33-
import rocks.didit.sefilm.database.ImdbIdConverter
34-
import rocks.didit.sefilm.database.TmdbIdConverter
3529
import rocks.didit.sefilm.database.entities.BioBudord
3630
import rocks.didit.sefilm.database.entities.Location
3731
import rocks.didit.sefilm.database.repositories.BudordRepository
3832
import rocks.didit.sefilm.database.repositories.LocationRepository
3933
import rocks.didit.sefilm.database.repositories.MovieRepository
34+
import rocks.didit.sefilm.database.repositories.ShowingRepository
35+
import rocks.didit.sefilm.domain.Base64ID
4036
import rocks.didit.sefilm.domain.ExternalProviderErrorHandler
4137
import rocks.didit.sefilm.graphql.GraphqlExceptionHandler
4238
import rocks.didit.sefilm.services.SFService
39+
import rocks.didit.sefilm.services.SlugService
4340
import rocks.didit.sefilm.utils.MovieFilterUtil
41+
import rocks.didit.sefilm.web.controllers.CalendarController
4442
import java.math.BigDecimal
45-
import java.time.Duration
4643

4744
@SpringBootApplication
48-
@EnableMongoHttpSession(maxInactiveIntervalInSeconds = 172800) // 48 hours
49-
//@EnableMongoAuditing // TODO enable when it works together with EnableMongoHttpSession
5045
@EnableCaching
5146
@EnableWebSecurity
5247
@EnableAsync
@@ -59,39 +54,37 @@ class Application {
5954
const val API_BASE_PATH = "/api"
6055
}
6156

62-
@Bean
63-
fun mongoSessionConverter(): AbstractMongoSessionConverter = JdkMongoSessionConverter(Duration.ofSeconds(43200))
64-
65-
@Bean
66-
fun customMongoConverters(): MongoCustomConversions {
67-
val converters = listOf(ImdbIdConverter(), TmdbIdConverter())
68-
return MongoCustomConversions(converters)
69-
}
70-
7157
@Bean
7258
@Primary
73-
fun getRestClient(): RestTemplate {
59+
fun getRestClient(errorHandler: ExternalProviderErrorHandler): RestTemplate {
7460
val restClient = RestTemplate()
75-
restClient.errorHandler = ExternalProviderErrorHandler()
61+
restClient.errorHandler = errorHandler
7662
return restClient
7763
}
7864

7965
@Bean
8066
fun getHttpEntityWithJsonAcceptHeader(): HttpEntity<Void> {
8167
val httpHeaders = HttpHeaders()
8268
httpHeaders.accept = listOf(MediaType.APPLICATION_JSON_UTF8)
83-
httpHeaders.put("User-Agent", listOf("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36"))
69+
httpHeaders.put(
70+
"User-Agent",
71+
listOf("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36")
72+
)
8473
return HttpEntity(httpHeaders)
8574
}
8675

8776
@Bean
88-
fun corsConfigurer(): WebMvcConfigurer {
77+
fun corsConfigurer(properties: Properties): WebMvcConfigurer {
8978
return object : WebMvcConfigurer {
9079
override fun addCorsMappings(registry: CorsRegistry) {
91-
registry.addMapping("/**")
92-
.allowedOrigins("http://localhost:3000", "https://bio.didit.rocks")
93-
.allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD")
94-
.allowCredentials(true)
80+
registry.addMapping("/graphql")
81+
.allowedOrigins(properties.baseUrl.frontend, properties.baseUrl.api)
82+
.allowedMethods("GET", "POST", "HEAD")
83+
.allowCredentials(false)
84+
registry.addMapping("${CalendarController.PATH}/**")
85+
.allowedOrigins(properties.baseUrl.frontend, properties.baseUrl.api)
86+
.allowedMethods("GET", "HEAD")
87+
.allowCredentials(false)
9588
}
9689
}
9790
}
@@ -105,6 +98,20 @@ class Application {
10598
movieRepository.deleteAll(unwantedMovies)
10699
}
107100

101+
@Bean
102+
fun createSlugsAndWebIds(showingRepository: ShowingRepository, slugService: SlugService) = ApplicationRunner {
103+
val showingsWithMissingWebId = showingRepository
104+
.findAll()
105+
.filter {
106+
it.webId == Base64ID.MISSING
107+
}
108+
109+
val updatedShowings = showingsWithMissingWebId.map {
110+
it.copy(webId = Base64ID.random(), slug = slugService.generateSlugFor(it))
111+
}
112+
showingRepository.saveAll(updatedShowings)
113+
}
114+
108115
@Bean
109116
fun trimMovieNames(movieRepository: MovieRepository, titleExtensions: MovieFilterUtil) = ApplicationRunner {
110117
movieRepository.findAll()
@@ -119,11 +126,15 @@ class Application {
119126
}
120127
}
121128

129+
private data class LocationAliasDTO(val sfId: String, val alias: List<String>)
130+
122131
@Bean
123-
fun seedInitialData(locationsRepo: LocationRepository,
124-
budordRepo: BudordRepository,
125-
sfService: SFService,
126-
properties: Properties) = ApplicationRunner {
132+
fun seedInitialData(
133+
locationsRepo: LocationRepository,
134+
budordRepo: BudordRepository,
135+
sfService: SFService,
136+
properties: Properties
137+
) = ApplicationRunner {
127138
if (!properties.enableSeeding) {
128139
log.info("Seeding not enabled, ignoring...")
129140
return@ApplicationRunner
@@ -142,27 +153,35 @@ class Application {
142153
val locationsResource = ClassPathResource("seeds/locations.json")
143154
val locations: List<Location> = objectMapper.readValue(locationsResource.inputStream)
144155

145-
val cityAlias = properties.defaultCity
146-
val locationsFromSF = sfService.getLocationsInCity(cityAlias)
156+
val locationNameAliasResource = ClassPathResource("seeds/sf-location-aliases.json")
157+
val locationNameAlias: List<LocationAliasDTO> = objectMapper.readValue(locationNameAliasResource.inputStream)
158+
159+
val defaultCity = properties.defaultCity
160+
val locationsFromSF = sfService.getLocationsInCity(properties.defaultCity)
147161
.map {
148-
Location(it.title,
162+
Location(
163+
it.title,
149164
it.address.city["alias"],
150165
it.address.city["name"],
151166
it.address.streetAddress,
152167
it.address.postalCode,
153168
it.address.postalAddress,
154169
BigDecimal(it.address.coordinates.latitude),
155-
BigDecimal(it.address.coordinates.longitude))
170+
BigDecimal(it.address.coordinates.longitude),
171+
it.ncgId,
172+
alias = locationNameAlias.firstOrNull() { alias -> alias.sfId == it.ncgId }?.alias ?: listOf()
173+
)
156174
}
157175

158176
locationsRepo.saveAll(locations)
159177
log.info("Seeded locations with ${locations.size} values")
160178

161-
log.info("Seeded ${locationsRepo.saveAll(locationsFromSF).count()} locations from SF for city: $cityAlias")
179+
log.info("Seeded ${locationsRepo.saveAll(locationsFromSF).count()} locations from SF for city: $defaultCity")
162180
}
163181

164182
@Bean
165-
fun graphqlExecutionStrategy(graphqlExceptionHandler: GraphqlExceptionHandler) = AsyncExecutionStrategy(graphqlExceptionHandler)
183+
fun graphqlExecutionStrategy(graphqlExceptionHandler: GraphqlExceptionHandler) =
184+
AsyncExecutionStrategy(graphqlExceptionHandler)
166185

167186
@Bean
168187
fun graphQLServletObjectMapperConfigurer(): ObjectMapperConfigurer {
@@ -183,8 +202,7 @@ class Application {
183202
}
184203

185204
@Bean
186-
fun schemaParserOptions(objConfigurer: com.coxautodev.graphql.tools.ObjectMapperConfigurer)
187-
= SchemaParserOptions
205+
fun schemaParserOptions(objConfigurer: com.coxautodev.graphql.tools.ObjectMapperConfigurer) = SchemaParserOptions
188206
.newOptions()
189207
.objectMapperConfigurer(objConfigurer)
190208
.build()

backend/src/main/kotlin/rocks/didit/sefilm/Exceptions.kt

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,40 +7,54 @@ import rocks.didit.sefilm.domain.UserID
77
import java.util.*
88

99
@ResponseStatus(HttpStatus.NOT_FOUND)
10-
class NotFoundException(what: String, userID: UserID? = null, showingId: UUID? = null) : KnownException("Could not find $what", userID, showingId)
10+
class NotFoundException(what: String, userID: UserID? = null, showingId: UUID? = null) :
11+
KnownException("Could not find $what", userID, showingId)
1112

1213
@ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
13-
class MissingPhoneNumberException(userID: UserID) : KnownException("User is missing a phone number", userID)
14+
class MissingPhoneNumberException(userID: UserID) :
15+
KnownException("You are missing a phone number.", userID)
1416

1517
@ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
1618
class MissingParametersException(what: String = "") : KnownException("Some required parameters were missing: " + what)
1719

1820
@ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
19-
class ExternalProviderException(msg: String) : KnownException("Unable to fetch information from external provider: $msg")
21+
class ExternalProviderException(msg: String) :
22+
KnownException("Unable to fetch information from external provider: $msg")
2023

2124
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
2225
class MissingAPIKeyException(service: String) : KnownException("The service $service is missing an API key")
2326

2427
@ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
25-
class TicketsAlreadyBoughtException(userID: UserID, showingId: UUID) : KnownException("The action is not allowed since the tickets for this showing is already bought", userID, showingId)
28+
class TicketsAlreadyBoughtException(userID: UserID, showingId: UUID) :
29+
KnownException("The action is not allowed since the tickets for this showing is already bought", userID, showingId)
2630

27-
class TicketAlreadyInUserException(userID: UserID) : KnownException("One or more of your företagsbiljeter is already in use", userID)
31+
class TicketAlreadyInUserException(userID: UserID) :
32+
KnownException("One or more of your företagsbiljeter is already in use", userID)
2833

2934
@ResponseStatus(HttpStatus.BAD_REQUEST)
30-
class UserAlreadyAttendedException(userID: UserID) : KnownException("The user has already attended this showing", userID)
35+
class UserAlreadyAttendedException(userID: UserID) :
36+
KnownException("The user has already attended this showing", userID)
3137

3238
@ResponseStatus(HttpStatus.BAD_REQUEST)
3339
class TicketNotFoundException(ticketNumber: TicketNumber) : KnownException("Ticket " + ticketNumber + " not found")
3440

3541
@ResponseStatus(HttpStatus.BAD_REQUEST)
3642
class DuplicateTicketException(msg: String = "") : KnownException("Found duplicate tickets" + msg)
3743

44+
class TicketAlreadyUsedException(whichTicket: TicketNumber) :
45+
KnownException("The ticket $whichTicket has already been used")
46+
47+
class TicketExpiredException(whichTicket: TicketNumber) :
48+
KnownException("The ticket $whichTicket has expired or will expire before the showing will be bought")
49+
50+
class TicketInUseException(whichTicket: TicketNumber) :
51+
KnownException("The ticket $whichTicket is already in use on a showing and cannot be removed")
52+
3853
@ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
3954
class SfTicketException(msg: String) : KnownException(msg)
4055

41-
@ResponseStatus(HttpStatus.BAD_REQUEST)
42-
class BadRequestException(msg: String) : KnownException(msg)
43-
44-
open class KnownException(msg: String,
45-
val whichUser: UserID? = null,
46-
val whichShowing: UUID? = null) : RuntimeException(msg)
56+
open class KnownException(
57+
msg: String,
58+
val whichUser: UserID? = null,
59+
val whichShowing: UUID? = null
60+
) : RuntimeException(msg)

backend/src/main/kotlin/rocks/didit/sefilm/Extensions.kt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
package rocks.didit.sefilm
22

3+
import org.springframework.security.core.Authentication
34
import org.springframework.security.core.context.SecurityContextHolder
4-
import org.springframework.security.oauth2.common.OAuth2AccessToken
55
import rocks.didit.sefilm.domain.IMDbID
66
import rocks.didit.sefilm.domain.TMDbID
77
import rocks.didit.sefilm.domain.UserID
88

99
internal fun currentLoggedInUser(): UserID {
10-
val principal = SecurityContextHolder.getContext().authentication.principal as OpenIdConnectUserDetails
11-
return UserID(principal.userId)
12-
}
10+
val authentication: Authentication? = SecurityContextHolder.getContext().authentication
11+
if (authentication?.isAuthenticated != true) {
12+
throw IllegalStateException("Cannot get current user if user isn't authenticated")
13+
}
1314

14-
internal fun oauthAccessToken(): OAuth2AccessToken {
15-
val principal = SecurityContextHolder.getContext().authentication.principal as OpenIdConnectUserDetails
16-
return principal.accessToken
15+
val principal = authentication.principal as OpenIdConnectUserDetails
16+
return UserID(principal.userId)
1717
}
1818

1919
internal fun String.toImdbId() = IMDbID.valueOf(this)

backend/src/main/kotlin/rocks/didit/sefilm/Properties.kt

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ class Properties {
1212
var defaultCity: String = "GB"
1313

1414
var google = Google()
15-
var login = Login()
1615

1716
class Tmdb {
1817
var apikey: String? = null
@@ -23,15 +22,7 @@ class Properties {
2322

2423
class Google {
2524
var clientId: String = ""
26-
var clientSecret: String = ""
27-
var accessTokenUri: String = ""
28-
var userAuthorizationUri: String = ""
29-
var redirectUri: String = ""
30-
}
31-
32-
class Login {
33-
var baseRedirectUri: String = "http://localhost:3000"
34-
var defaultRedirectPath: String = "user"
25+
var jwkUri: String = ""
3526
}
3627

3728
class BaseUrl {

0 commit comments

Comments
 (0)