Lets say we modified an event to have a new field added. I understand that we can handle the serialization for event mapping changes in this documentation https://www.lagomframework.com/documentation/1.5.x/scala/Serialization.html but how does lagom know which version the event is? When declaring and defining case classes events, we do not specify the event version. So how does lagom serialization know which event version mapping to use?
In the image below, there is a field called fromVersion. How does lagom know the current version of events pulled from event store datastore?
So, to implement migration you add following code:
private val itemAddedMigration = new JsonMigration(2) {
override def transform(fromVersion: Int, json: JsObject): JsObject = {
if (fromVersion < 2) {
json + ("discount" -> JsNumber(0.0d))
} else {
json
}
}
}
override def migrations = Map[String, JsonMigration](
classOf[ItemAdded].getName -> itemAddedMigration
)
}
That means that all new events of type ItemAdded now will have version 2. All previous events will be treated as version 1.
It is defined in the class PlayJsonSerializer
Please see the following code:
private def parseManifest(manifest: String) = {
val i = manifest.lastIndexOf('#')
val fromVersion = if (i == -1) 1 else manifest.substring(i + 1).toInt
val manifestClassName = if (i == -1) manifest else manifest.substring(0, i)
(fromVersion, manifestClassName)
}
Also, you can check it in the database. I use Cassandra, I if I will open my database, in eventsbytag1 collection I can find the field ser_manifest where described version. Where is simple class - it is version 1, where you have specified additional '#2', it means version 2 and so on.
If you need more information about how it works, you can check method fromBinary in class PlayJsonSerializer.
Related
I need to expose some models which don't used directly in REST API methods.
With springfox I used Docket's additionalModels method to programmatically add models to specification:
docket.additionalModels(
typeResolver.resolve(XModel1.class),
typeResolver.resolve(XModel2.class)
)
How to do it with springdoc?
I've created a dummy operation with dummy-parameter which includes all required models. But I feel the approach has space for improvement.
With OpenApiCustomiser , you have access to the OpenAPI Object.
You can add any object/operation you want without having to add annotations on your code.
You can have a look at the documentation for more details:
https://springdoc.org/#how-can-i-customise-the-openapi-object
In Kotlin
fun components(): Components {
val components = Components()
val converter = ModelConverters.getInstance()
val schema1 = converter.readAllAsResolvedSchema(XModel1::class.java)
val schema2 = converter.readAllAsResolvedSchema(XModel2::class.java)
schema1.referencedSchemas.forEach { s -> components.addSchemas(s.key, s.value) }
schema2.referencedSchemas.forEach { s -> components.addSchemas(s.key, s.value) }
return components
}
Additionally you may need to specify the property in application.yml:
springdoc:
remove-broken-reference-definitions: false
I am new to Lagom and I want to deal with Event Sourcing at some later point. So I'm not using the persistentEntityRegistry, instead I'm using a simple repository for CRUD operations.
Anyway I would like to have the ability to notify other Services, when a create operation has happened.
In the hello-lagom project the topic is implemented like this:
override def greetingsTopic(): Topic[GreetingMessage] =
TopicProducer.singleStreamWithOffset {
fromOffset =>
persistentEntityRegistry.eventStream(HelloEventTag.INSTANCE, fromOffset)
.map(ev => (convertEvent(ev), ev.offset))
}
This will obviously not work, when I'm not working without the event sourcing, so I just wondered if there is another way to publish events to the topic.
I'm thinking about sth. like this:
override def createSth = ServiceCall { createCommandData =>
val id = UUID.randomUUID()
repository.addSth(Sth(id, createCommandData.someValue)) map {createdItem =>
myTopic.publish(SthWasCreated(id))
}
}
I have a Object like this:
// I want to test this Object
object MyObject {
protected val retryHandler: HttpRequestRetryHandler = new HttpRequestRetryHandler {
def retryRequest(exception: IOException, executionCount: Int, context: HttpContext): Boolean = {
true // implementation
}
}
private val connectionManager: PoolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager
val httpClient: CloseableHttpClient = HttpClients.custom
.setConnectionManager(connectionManager)
.setRetryHandler(retryHandler)
.build
def methodPost = {
//create new context and new Post instance
val post = new HttpPost("url")
val res = httpClient.execute(post, HttpClientContext.create)
// check response code and then take action based on response code
}
def methodPut = {
// same as methodPost except use HttpPut instead HttpPost
}
}
I want to test this object by mocking dependent objects like httpClient. How to achieve this? can i do it using Mokito or any better way? If yes. How? Is there a better design for this class?
Your problem is: you created hard-to test code. You can turn here to watch some videos to understand why that is.
The short answer: directly calling new in your production code always makes testing harder. You could be using Mockito spies (see here on how that works).
But: the better answer would be to rework your production code; for example to use dependency injection. Meaning: instead of creating the objects your class needs itself (by using new) ... your class receives those objects from somewhere.
The typical (java) approach would be something like:
public MyClass() { this ( new SomethingINeed() ); }
MyClass(SomethingINeed incoming) { this.somethign = incoming; }
In other words: the normal usage path still calls new directly; but for unit testing you provide an alternative constructor that you can use to inject the thing(s) your class under test depends on.
In Cucumber, how do i go about passing variables between step definition classes. Im trying to implement in Scala.
Looking around I have seen people suggest using Guice or Picocontainer or any other DI framework. But have not really come across an example in Scala.
For instance for the example below how do I pass the variable using DI ?
Provider.scala,
class Provider extends ScalaDsl with EN with Matchers with WebBrowser {
......
When("""I click the Done button$""") {
val doneButton = getElement(By.id(providerConnectionButton))
doneButton.click()
}
Then("""a new object should be created successfully""") {
// Pass the provider ID created in this step to Consumer definition
}
}
Consumer.scala,
class Consumer extends ScalaDsl with EN with Matchers with WebBrowser {
......
When("""^I navigate to Consumer page$""") { () =>
// providerId is the id from Provider above
webDriver.navigate().to(s"${configureUrl}${providerId}")
}
}
You can use ThreadLocal to solve your problem
Here's code snippet for solution.
object IDProvider{
val providerId = new ThreadLocal[String]
def getProviderId: String = {
providerId.get()
}
def setProviderId(providerId: String): Unit = {
providerId.set(providerId)
}
}
To access providerID across different step definitions. You can simply call IDProvider.getProviderId
And to set the value of providerID, simply call IDProvider.setProviderId(PROVIDER_ID)
I'm trying to add a custom GORM event listener class in Bootstrap.groovy, as described in the Grails documentation but its not working for me. Here is the code straight from the docs:
def init = {
application.mainContext.eventTriggeringInterceptor.datastores.each { k, datastore ->
applicationContext.addApplicationListener new MyPersistenceListener(datastore)
}
}
When I run it, the compiler complains that application and applicationContext are null. I've tried adding them as class level members but they don't get magically wired up service-style. The closest I've got so far is:
def grailsApplication
def init = { servletContext ->
def applicationContext = servletContext.getAttribute(ApplicationAttributes.APPLICATION_CONTEXT)
grailsApplication.mainContext.eventTriggeringInterceptor.datastores.each { k, datastore ->
applicationContext.addApplicationListener new GormEventListener(datastore)
}
}
But I still get errors: java.lang.NullPointerException: Cannot get property 'datastores' on null object.
Thanks for reading...
EDIT: version 2.2.1
If you do:
ctx.getBeansOfType(Datastore).values().each { Datastore d ->
ctx.addApplicationListener new MyPersistenceListener(d)
}
This should work without needing the Hibernate plugin installed
That looks like it should work, although I'd do it a bit differently. BootStrap.groovy does support dependency injection, so you can inject the grailsApplication bean, but you can also inject eventTriggeringInterceptor directly:
class BootStrap {
def grailsApplication
def eventTriggeringInterceptor
def init = { servletContext ->
def ctx = grailsApplication.mainContext
eventTriggeringInterceptor.datastores.values().each { datastore ->
ctx.addApplicationListener new MyPersistenceListener(datastore)
}
}
}
Here I still inject grailsApplication but only because I need access to the ApplicationContext to register listeners. Here's my listener (simpler than what the docs claim the simplest implementation would be btw ;)
import org.grails.datastore.mapping.core.Datastore
import org.grails.datastore.mapping.engine.event.AbstractPersistenceEvent
import org.grails.datastore.mapping.engine.event.AbstractPersistenceEventListener
class MyPersistenceListener extends AbstractPersistenceEventListener {
MyPersistenceListener(Datastore datastore) {
super(datastore)
}
protected void onPersistenceEvent(AbstractPersistenceEvent event) {
println "Event $event.eventType $event.entityObject"
}
boolean supportsEventType(Class eventType) { true }
}
Finally stumbled onto a working Bootstrap.groovy, thanks to this post but I don't think its the best way to do it, rather its a work around.
def init = { servletContext ->
def applicationContext = servletContext.getAttribute(ApplicationAttributes.APPLICATION_CONTEXT)
applicationContext.addApplicationListener new GormEventListener(applicationContext.mongoDatastore)
}
So basically I'm hard-coding the MongoDB datastore directly as opposed to iterating over the available ones, as the docs suggest.
To save you reading the comments to the first answer, the adapted version I provided in the Question (as well as Burt's answer) only works if the Hibernate plugin is installed but in my case I was using the MongoDB plugin so had no need for the Hibernate plugin (it in fact broke my app in other ways).