-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat(backend): Mediator implementation for Kotlin. - added notification, pipeline, command and query support * feat(backend): Basic Subscriber Register Process * feat(backend): Basic Subscriber Register Process * test(backend): email send test * style(core): updated and formated frontend and backend dependencies * build(core): split docker compose files * build(core): split docker compose files * build(core): example env variables * build(core): added spring boot library convention * test(criteria): testing the criteria layer * test(criteria): testing the criteria parser * test(criteria): testing the criteria parser * test(criteria): testing event configuration and emitter
- Loading branch information
1 parent
abf8ebd
commit 0c8cfda
Showing
158 changed files
with
6,233 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
COMPOSE_PATH_SEPARATOR=: | ||
COMPOSE_FILE=postgresql-compose.yml:docker-compose.yml | ||
|
||
SENDGRID_API_KEY=YOUR_API_KEY |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -53,7 +53,6 @@ yarn-error.log* | |
pnpm-debug.log* | ||
|
||
# environment variables | ||
.env | ||
.env.production | ||
|
||
# macOS-specific files | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
61 changes: 61 additions & 0 deletions
61
apps/backend/src/main/kotlin/com/lyra/app/config/db/DatabaseConfig.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package com.lyra.app.config.db | ||
|
||
import com.lyra.app.newsletter.domain.SubscriberStatus | ||
import com.lyra.app.newsletter.infrastructure.persistence.converter.SubscriberConverter | ||
import com.lyra.app.newsletter.infrastructure.persistence.converter.SubscriberStatusWriterConverter | ||
import io.r2dbc.postgresql.codec.EnumCodec | ||
import io.r2dbc.postgresql.codec.EnumCodec.Builder.RegistrationPriority | ||
import io.r2dbc.postgresql.extension.CodecRegistrar | ||
import io.r2dbc.spi.ConnectionFactoryOptions | ||
import io.r2dbc.spi.Option | ||
import org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryOptionsBuilderCustomizer | ||
import org.springframework.context.annotation.Bean | ||
import org.springframework.context.annotation.Configuration | ||
import org.springframework.data.convert.CustomConversions | ||
import org.springframework.data.r2dbc.config.EnableR2dbcAuditing | ||
import org.springframework.data.r2dbc.convert.R2dbcCustomConversions | ||
import org.springframework.data.r2dbc.dialect.DialectResolver | ||
import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories | ||
import org.springframework.r2dbc.core.DatabaseClient | ||
import org.springframework.transaction.annotation.EnableTransactionManagement | ||
|
||
@Configuration | ||
@EnableTransactionManagement | ||
@EnableR2dbcRepositories | ||
@EnableR2dbcAuditing | ||
class DatabaseConfig { | ||
/** | ||
* Use the customizer to add EnumCodec to R2DBC | ||
*/ | ||
@Bean | ||
fun connectionFactoryOptionsBuilderCustomizer(): ConnectionFactoryOptionsBuilderCustomizer { | ||
return ConnectionFactoryOptionsBuilderCustomizer { builder: ConnectionFactoryOptions.Builder -> | ||
builder.option( | ||
Option.valueOf("extensions"), | ||
listOf<CodecRegistrar>( | ||
EnumCodec.builder() | ||
.withEnum("subscriber_status", SubscriberStatus::class.java) | ||
.withRegistrationPriority(RegistrationPriority.FIRST) | ||
.build(), | ||
), | ||
) | ||
} | ||
} | ||
|
||
/** | ||
* Register converter to make sure Spring data treat enum correctly | ||
*/ | ||
@Bean | ||
fun r2dbcCustomConversions(databaseClient: DatabaseClient): R2dbcCustomConversions { | ||
val dialect = DialectResolver.getDialect(databaseClient.connectionFactory) | ||
val converters: MutableList<Any?> = ArrayList(dialect.converters) | ||
converters.addAll(R2dbcCustomConversions.STORE_CONVERTERS) | ||
return R2dbcCustomConversions( | ||
CustomConversions.StoreConversions.of(dialect.simpleTypeHolder, converters), | ||
listOf( | ||
SubscriberConverter(), | ||
SubscriberStatusWriterConverter(), | ||
), | ||
) | ||
} | ||
} |
70 changes: 70 additions & 0 deletions
70
apps/backend/src/main/kotlin/com/lyra/app/controllers/GlobalExceptionHandler.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package com.lyra.app.controllers | ||
|
||
import com.lyra.common.domain.error.BusinessRuleValidationException | ||
import java.net.URI | ||
import java.time.Instant | ||
import org.springframework.http.HttpStatus | ||
import org.springframework.http.ProblemDetail | ||
import org.springframework.web.bind.annotation.ExceptionHandler | ||
import org.springframework.web.bind.annotation.ResponseStatus | ||
import org.springframework.web.bind.annotation.RestControllerAdvice | ||
import org.springframework.web.reactive.result.method.annotation.ResponseEntityExceptionHandler | ||
|
||
private const val ERROR_PAGE = "https://lyra.io/errors" | ||
|
||
/** | ||
* This class provides a global exception handling mechanism for the application. | ||
* | ||
* It extends the [ResponseEntityExceptionHandler] class to handle exceptions and return appropriate responses. | ||
* | ||
* @created 4/8/23 | ||
*/ | ||
@RestControllerAdvice | ||
class GlobalExceptionHandler : ResponseEntityExceptionHandler() { | ||
/** | ||
* Handles the [UserAuthenticationException] by creating a ProblemDetail object with the appropriate status, | ||
* detail and properties. | ||
* | ||
* @param e The UserAuthenticationException that was thrown. | ||
* @return The ProblemDetail object representing the exception. | ||
*/ | ||
// @ResponseStatus(HttpStatus.UNAUTHORIZED) | ||
// @ExceptionHandler(UserAuthenticationException::class) | ||
// fun handleUserAuthenticationException(e: UserAuthenticationException): ProblemDetail { | ||
// val problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.UNAUTHORIZED, e.message) | ||
// problemDetail.title = "User authentication failed" | ||
// problemDetail.setType(URI.create("$ERROR_PAGE/user-authentication-failed")) | ||
// problemDetail.setProperty("errorCategory", "AUTHENTICATION") | ||
// problemDetail.setProperty("timestamp", Instant.now()) | ||
// return problemDetail | ||
// } | ||
|
||
@ResponseStatus(HttpStatus.BAD_REQUEST) | ||
@ExceptionHandler( | ||
IllegalArgumentException::class, | ||
BusinessRuleValidationException::class, | ||
// UserRefreshTokenException::class | ||
) | ||
fun handleIllegalArgumentException(e: Exception): ProblemDetail { | ||
val problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST, e.message ?: "Bad request") | ||
problemDetail.title = "Bad request" | ||
problemDetail.setType(URI.create("$ERROR_PAGE/bad-request")) | ||
problemDetail.setProperty("errorCategory", "BAD_REQUEST") | ||
problemDetail.setProperty("timestamp", Instant.now()) | ||
return problemDetail | ||
} | ||
|
||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) | ||
@ExceptionHandler(Exception::class) | ||
fun handleException(e: Exception): ProblemDetail { | ||
val problemDetail = ProblemDetail.forStatusAndDetail( | ||
HttpStatus.INTERNAL_SERVER_ERROR, | ||
e.message ?: "Internal server error", | ||
) | ||
problemDetail.title = "Internal server error" | ||
problemDetail.setType(URI.create("$ERROR_PAGE/internal-server-error")) | ||
problemDetail.setProperty("errorCategory", "INTERNAL_SERVER_ERROR") | ||
problemDetail.setProperty("timestamp", Instant.now()) | ||
return problemDetail | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
...ain/kotlin/com/lyra/app/newsletter/application/CreateSubscribeNewsletterCommandHandler.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package com.lyra.app.newsletter.application | ||
|
||
import com.lyra.app.newsletter.domain.FirstName | ||
import com.lyra.app.newsletter.domain.LastName | ||
import com.lyra.app.newsletter.domain.Name | ||
import com.lyra.app.newsletter.domain.SubscriberId | ||
import com.lyra.common.domain.Service | ||
import com.lyra.common.domain.bus.command.CommandHandler | ||
import com.lyra.common.domain.email.Email | ||
|
||
@Service | ||
class CreateSubscribeNewsletterCommandHandler( | ||
private val subscriberRegistrator: SubscriberRegistrator | ||
) : CommandHandler<SubscribeNewsletterCommand> { | ||
override suspend fun handle(command: SubscribeNewsletterCommand) { | ||
val id = SubscriberId(command.id) | ||
val email = Email(command.email) | ||
val name = Name(FirstName(command.firstname), command.lastname?.let { LastName(it) }) | ||
subscriberRegistrator.register(id, email, name) | ||
} | ||
} |
14 changes: 14 additions & 0 deletions
14
...backend/src/main/kotlin/com/lyra/app/newsletter/application/SubscribeNewsletterCommand.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.lyra.app.newsletter.application | ||
|
||
import com.lyra.common.domain.bus.command.Command | ||
|
||
/** | ||
* | ||
* @created 7/1/24 | ||
*/ | ||
data class SubscribeNewsletterCommand( | ||
val id: String, | ||
val email: String, | ||
val firstname: String, | ||
val lastname: String? | ||
) : Command |
45 changes: 45 additions & 0 deletions
45
apps/backend/src/main/kotlin/com/lyra/app/newsletter/application/SubscriberRegistrator.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package com.lyra.app.newsletter.application | ||
|
||
import com.lyra.app.newsletter.domain.Name | ||
import com.lyra.app.newsletter.domain.Subscriber | ||
import com.lyra.app.newsletter.domain.SubscriberId | ||
import com.lyra.app.newsletter.domain.SubscriberRepository | ||
import com.lyra.app.newsletter.domain.event.SubscriberCreatedEvent | ||
import com.lyra.common.domain.Service | ||
import com.lyra.common.domain.bus.event.EventBroadcaster | ||
import com.lyra.common.domain.bus.event.EventPublisher | ||
import com.lyra.common.domain.email.Email | ||
import org.slf4j.LoggerFactory | ||
|
||
/** | ||
* | ||
* @created 6/1/24 | ||
*/ | ||
@Service | ||
class SubscriberRegistrator( | ||
private val subscriberRepository: SubscriberRepository, | ||
eventPublisher: EventPublisher<SubscriberCreatedEvent> | ||
) { | ||
private val eventPublisher = EventBroadcaster<SubscriberCreatedEvent>() | ||
|
||
init { | ||
this.eventPublisher.use(eventPublisher) | ||
} | ||
|
||
suspend fun register(id: SubscriberId, email: Email, name: Name) { | ||
log.info("Registering subscriber with email: $email") | ||
|
||
val subscriber = Subscriber.create(id, email, name) | ||
subscriberRepository.create(subscriber) | ||
val domainEvents = subscriber.pullDomainEvents() | ||
log.debug("Pulling {} events from subscriber", domainEvents.size) | ||
|
||
domainEvents.forEach { | ||
eventPublisher.publish(it as SubscriberCreatedEvent) | ||
} | ||
} | ||
|
||
companion object { | ||
private val log = LoggerFactory.getLogger(SubscriberRegistrator::class.java) | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
apps/backend/src/main/kotlin/com/lyra/app/newsletter/application/SubscribersResponse.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package com.lyra.app.newsletter.application | ||
|
||
import com.lyra.common.domain.bus.query.Response | ||
|
||
data class SubscribersResponse(val subscribers: List<SubscriberResponse>) : Response | ||
|
||
data class SubscriberResponse( | ||
val id: String, | ||
val email: String, | ||
val name: String, | ||
val status: String, | ||
) : Response |
37 changes: 37 additions & 0 deletions
37
...main/kotlin/com/lyra/app/newsletter/application/search/active/ActiveSubscriberSearcher.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package com.lyra.app.newsletter.application.search.active | ||
|
||
import com.lyra.app.newsletter.application.SubscriberResponse | ||
import com.lyra.app.newsletter.application.SubscribersResponse | ||
import com.lyra.app.newsletter.domain.Subscriber | ||
import com.lyra.app.newsletter.domain.SubscriberRepository | ||
import com.lyra.common.domain.Service | ||
import kotlinx.coroutines.flow.Flow | ||
import kotlinx.coroutines.flow.map | ||
import kotlinx.coroutines.flow.toList | ||
import org.slf4j.LoggerFactory | ||
|
||
/** | ||
* | ||
* @created 10/1/24 | ||
*/ | ||
@Service | ||
class ActiveSubscriberSearcher(private val repository: SubscriberRepository) { | ||
|
||
suspend fun search(): SubscribersResponse { | ||
log.info("Searching active subscribers") | ||
val subscribers: Flow<Subscriber> = repository.searchActive() | ||
return SubscribersResponse( | ||
subscribers.map { | ||
SubscriberResponse( | ||
it.id.toString(), | ||
it.email.value, | ||
it.name.fullName(), | ||
it.status.name, | ||
) | ||
}.toList(), | ||
) | ||
} | ||
companion object { | ||
private val log = LoggerFactory.getLogger(ActiveSubscriberSearcher::class.java) | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
...tlin/com/lyra/app/newsletter/application/search/active/SearchAllActiveSubscribersQuery.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package com.lyra.app.newsletter.application.search.active | ||
|
||
import com.lyra.app.newsletter.application.SubscribersResponse | ||
import com.lyra.common.domain.bus.query.Query | ||
|
||
/** | ||
* | ||
* @created 9/1/24 | ||
*/ | ||
class SearchAllActiveSubscribersQuery : Query<SubscribersResponse> { | ||
override fun equals(other: Any?): Boolean { | ||
if (this === other) return true | ||
if (other !is SearchAllActiveSubscribersQuery) return false | ||
return true | ||
} | ||
|
||
override fun hashCode(): Int = javaClass.hashCode() | ||
} |
23 changes: 23 additions & 0 deletions
23
...m/lyra/app/newsletter/application/search/active/SearchAllActiveSubscribersQueryHandler.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package com.lyra.app.newsletter.application.search.active | ||
|
||
import com.lyra.app.newsletter.application.SubscribersResponse | ||
import com.lyra.common.domain.Service | ||
import com.lyra.common.domain.bus.query.QueryHandler | ||
import org.slf4j.LoggerFactory | ||
|
||
/** | ||
* | ||
* @created 9/1/24 | ||
*/ | ||
@Service | ||
class SearchAllActiveSubscribersQueryHandler( | ||
private val searcher: ActiveSubscriberSearcher, | ||
) : QueryHandler<SearchAllActiveSubscribersQuery, SubscribersResponse> { | ||
override suspend fun handle(query: SearchAllActiveSubscribersQuery): SubscribersResponse { | ||
log.info("Searching all active subscribers") | ||
return searcher.search() | ||
} | ||
companion object { | ||
private val log = LoggerFactory.getLogger(SearchAllActiveSubscribersQueryHandler::class.java) | ||
} | ||
} |
Oops, something went wrong.