Implicit not found on store - phantom-dsl

I have followed the pattern from examples on GitHub. When I call store on the model object, passing an instance of the entity, I get a compile error indicating one of the implicit parameters is missing as shown below.
could not find implicit value for parameter sg: com.outworkers.phantom.macros.SingleGeneric.Aux[com.ss.wuhu.settlement.entity.Settlement,Repr,HL,Out]
I guess I am missing something obvious. Could someone please point out how to bring the implicit into scope?
Regards
Meeraj
This is the code snippet where I am storing the data.
import akka.Done
import com.outworkers.phantom.dsl._
import com.outworkers.phantom.connectors.{CassandraConnection, ContactPoints}
import com.ss.wuhu.settlement.entity.Settlement
import com.ss.wuhu.settlement.entity.mapping.{SettlementForCourierModel, SettlementForVendorModel}
object Connector {
private val hosts = Seq("127.0.0.1") // TODO from environment
lazy val connector: CassandraConnection = ContactPoints(hosts).keySpace("wuhu_order")
}
class SettlementDatabase(override val connector: CassandraConnection) extends Database[SettlementDatabase](connector) {
object SettlementForCourierModel extends SettlementForCourierModel with connector.Connector
object SettlementForVendorModel extends SettlementForVendorModel with connector.Connector
def truncateAll() = {
Database.truncate()
}
def store(set: Settlement) = {
for {
v <- Database.SettlementForVendorModel.store(set)
d <- Database.SettlementForCourierModel.store(set)
} yield (Done)
}
}
object Database extends SettlementDatabase(Connector.connector)

This is a known bug with an open issue: https://github.com/outworkers/phantom/issues/774
I suggest either using the workaround as described in the link above, or my workaround which was creating my own .store() using .insert().
example:
def myStore(person: Person) : Future[ResultSet] =
insert
.value(_.name, person.name)
.value(_.age, person.age)
.value(_.timeCreate, person.timeCreate)
.future()

Related

How to Access Mono<T> While Handling Exception with onErrorMap()?

In data class I defined the 'name' must be unique across whole mongo collection:
#Document
data class Inn(#Indexed(unique = true) val name: String,
val description: String) {
#Id
var id: String = UUID.randomUUID().toString()
var intro: String = ""
}
So in service I have to capture the unexpected exception if someone pass the same name again.
#Service
class InnService(val repository: InnRepository) {
fun create(inn: Mono<Inn>): Mono<Inn> =
repository
.create(inn)
.onErrorMap(
DuplicateKeyException::class.java,
{ err -> InnAlreadyExistedException("The inn already existed", err) }
)
}
This is OK, but what if I want to add more info to the exceptional message like "The inn named '$it.name' already existed", what should I do for transforming exception with enriched message.
Clearly, assign Mono<Inn> to a local variable at the beginning is not a good idea...
Similar situation in handler, I'd like to give client more info which derived from the customized exception, but no proper way can be found.
#Component
class InnHandler(val innService: InnService) {
fun create(req: ServerRequest): Mono<ServerResponse> {
return innService
.create(req.bodyToMono<Inn>())
.flatMap {
created(URI.create("/api/inns/${it.id}"))
.contentType(MediaType.APPLICATION_JSON_UTF8).body(it.toMono())
}
.onErrorReturn(
InnAlreadyExistedException::class.java,
badRequest().body(mapOf("code" to "SF400", "message" to t.message).toMono()).block()
)
}
}
In reactor, you aren't going to have the value you want handed to you in onErrorMap as an argument, you just get the Throwable. However, in Kotlin you can reach outside the scope of the error handler and just refer to inn directly. You don't need to change much:
fun create(inn: Mono<Inn>): Mono<Inn> =
repository
.create(inn)
.onErrorMap(
DuplicateKeyException::class.java,
{ InnAlreadyExistedException("The inn ${inn.name} already existed", it) }
)
}

Returning object of type specified in method arguments instead of AnyRef

I have the following method:
#org.springframework.stereotype.Service
class EntityCacheManager {
def get(cacheId: String, entityClass: Class[_]): AnyRef = { ... }
//...
}
So to use it, i have to write this:
val cachedEntity = entityCacheManager.get(cacheId, classOf[SomeEntity]).asInstanceOf[SomeEntity]
Is there some way to make EntityCacheManager.get() returning instance of type entityClass which is specified in method params? I'd like to avoid casting asInstanceOf every time i use this method. I know it would be nice to use generic definition of type EntityCacheManager, but it's also a spring-managed bean, so i think using generics will cause troubles.
You can use a more idiomatic scala approach by using the ClassTag typeclass
class EntityCacheManager {
def get[T: ClassTag](cacheId: String): T = {
val entityClass = implicitly[ClassTag[T]].runtimeClass
val myObject: T = ??? // you retrieve your object somehow using entityClass
myObject
}
}
you can now use it like this:
val myEntityClassInstance = get[MyEntityClass]("key")

Scala Generics - Overloaded method

Considering the given code:
val repository =
context.getBean(
Introspector.decapitalize(t.getClass.getSimpleName).replace("C", "E").concat("Repository"))
and that my repositories have a String as Serializable.
I'm trying to do the following:
repository.asInstanceOf[ElasticsearchRepository[_, String]].save(getObject(t))
This one works fine:
repository.asInstanceOf[ElasticsearchRepository[_, String]].findAll()
But I don't know how to put that above to work.
Assuming the method getObject(t) is retuning the correct object to be persisted and since it's a Spring Data Repository, there are 2 save method. One that accept a single entity and another for a list of entities and it says overloaded method value save.
What I have tried so far:
I saw in another thread to force the method with a type, something like this:
repository.asInstanceOf[ElasticsearchRepository[_, String]].save(getObject(t) : TYPE)
This is ok if I knew the type and also my method getObject should return that same type.
Here is my getObject method which I return the object itself without any specific type:
#throws[IOException]
def getObject[T](t : T) = {
objectMapper.readValue(objectMapper.writeValueAsString(t), getClazz(t))
}
So I was trying to get the type like this:
val m = Manifest.classType(getClazz(t))
type TYPE = m.type
Looks good if I force my object to this type using getObject(t) : TYPE but I don't know how to use this same type in my getObject method to be returned.
Anyway, I don't even know if this is the best approach to do this, invoking a generic repository and save a generic object.
Just to understand what I'm trying to do, I'm using a aspect to intercept a Cassandra entity to be persisted, then get it and turn into a ElasticSearch entity to save a json(thats why the getObject(t)) and replicate into ElasticSearch.
Here is the full aspect class:
#Component
#Aspect
class ElasticAop {
#Autowired val context : ApplicationContext = null
val objectMapper : ObjectMapper = new ObjectMapper()
#Pointcut("execution(* com.test.service.cassandra.*.post(..)) && args(t)")
def getPointcutPost[T](t : T) : Unit = {}
#throws[Throwable]
#Before("getPointcutPost(t)")
def elasticSaveAspect[T](joinPoint: JoinPoint, t: T) = {
val m = Manifest.classType(getClazz(t))
type TYPE = m.type
val repository =
context.getBean(
Introspector.decapitalize(t.getClass.getSimpleName).replace("C", "E").concat("Repository"))
repository.asInstanceOf[ElasticsearchRepository[_, String]].findAll()
repository.asInstanceOf[ElasticsearchRepository[_, String]].save(getObject(t))
}
#throws[ClassNotFoundException]
def getClazz[T](t : T) = {
val className = t.getClass.getName.replace("cassandra", "elastic").replace("C", "E")
Class.forName(className)
}
#throws[IOException]
def getObject[T](t : T) = {
objectMapper.readValue(objectMapper.writeValueAsString(t), getClazz(t))
}
}
EDITED
Even setting up a type return in my getObject to Address and then setting the save method as follow save(getObject(t) : Address) give me the same overloaded error.
EDITED
I just figured out it's a limitation and a possible work around is to create a factory or something like this.
Then I created a service with a saveOrUpdate method:
trait ElasticGenericService[T <: ElasticGenericKey, R <: ElasticsearchRepository[T, String]] {
var r : R = _
def saveOrUpdate(t: T) = r.save(t)
}
and now I'm getting a cast exception:
java.lang.ClassCastException: Address cannot be cast to scala.runtime.Nothing$
What i can see here:
getObject[T](t : T) returns existential type _1 and actually kills all type checks, as you choosing the class in runtime
ElasticsearchRepository[_, String].save require existential type _2 to be passed to the save method, so _1 doesn't fit
Possible solution:
repository.asInstanceOf[ElasticsearchRepository[Any, String]].save(getObject(t).asInstanceOf[Any]) //getClass will work with runtime class instead of Any, so should be fine
Another solution (saving existential type):
def getObject[T](t : T) = {
objectMapper.readValue(objectMapper.writeValueAsString(t), getClazz(t)).asInstanceOf[T]
} //assuming T is an existential - it will return same existential as you passed

Grails validate fields with default values

We have a class like this in a Grails 2.4.3 application (migrated from 2.3.8):
#Validateable
class Foo {
Integer noDefault;
Integer withDefault = 1;
static constraints = {
noDefault(nullable:false)
withDefault(nullable:false)
}
}
This class is being instantiated in a complex configuration mechanism using a Map like this:
[
noDefault: 0,
withDefault: 2
]
(In fact the Map is part of a huge one, but the class constructor sees this small one.) Formerly the class worked if we omitted the withDefault entry from the config map, using the default value which is not null. In Grails 2.4.3, however, it tells me that this field cannot be null. I can fix it by letting it be null in the constraint, but it lets setting the explicite value null (and overwrite the default value), which causes problem during operation.
Do you know some workaround, which preserves the semantics and correct operation?
Thanx in advance, best regards: Balázs
What you are describing is not consistent with what I would expect and not consistent with the behavior I am seeing. The project at https://github.com/jeffbrown/validatedefaults contains the following code.
At https://github.com/jeffbrown/validatedefaults/blob/master/src/groovy/demo/Foo.groovy
// src/groovy/demo/Foo.groovy
package demo
import grails.validation.Validateable
#Validateable
class Foo {
Integer noDefault;
Integer withDefault = 1;
static constraints = {
noDefault(nullable:false)
withDefault(nullable:false)
}
}
The test at https://github.com/jeffbrown/validatedefaults/blob/master/test/unit/demo/FooSpec.groovy passes:
// test/unit/demo/FooSpec.groovy
package demo
import spock.lang.Specification
import grails.test.mixin.TestMixin
import grails.test.mixin.support.GrailsUnitTestMixin
#TestMixin(GrailsUnitTestMixin)
class FooSpec extends Specification {
void 'test validating default values'() {
given:
def map = [noDefault: 0]
def foo = new Foo(map)
expect:
foo.validate()
}
}
When I run the app I get the same behavior.
// grails-app/conf/BootStrap.groovy
import demo.Foo
class BootStrap {
def init = { servletContext ->
def map = [noDefault: 0]
def foo = new Foo(map)
// this prints true...
println "Foo is valid? : ${foo.validate()}"
}
def destroy = {
}
}
I hope that helps.

Convert String into Class for TitleWindow

I don't know if this is possible, I am pulling the names for TitleWindows from my database as strings.
Then from my main application I have to launch the TitleWindow. So in my function I need to convert the name of the TitleWindow which is a String to a Class, because the PopUpManager accepts a Class. Below is my code.
When launching my application and trying to launch the TitleWindow I am getting the error:
Implicit coercion of a value of type String to an unrelated type Class.
I don't want to hard code the name of my popUp in the PopUpManager, that is why I am doing it like this. Any way to work around this?
public function getScreen(screenName:String):void
{
var screen_Name:Class = new Class();
screen_Name = screenName;
var popUpWindow:TitleWindow = PopUpManager.createPopUp(this, screen_Name, false) as TitleWindow;
PopUpManager.centerPopUp(popUpWindow);
}
I have had to do something very similar recently. Here is the function I wrote to do it:
//You have to provice the package signature
private var viewPackage:String = "org.bishop";
//In my case, event.type is the name of a class
var className: String = viewPackage + "." + event.type;
try{
var classRef:Class = getDefinitionByName(className) as Class;
viewNavigator.pushView(classRef);
}
catch(e:ViewError){
trace(e.message);
logger.debug(e.message);
}
Note: for the class to be created correctly, you will need to include both an import statement:
import org.bishop.Login;
and also declare a variable of the class in the code as follows:
Login;
otherwise the classes will not be available to be created.

Resources