I have an authorizaton Aspect that checks specific conditions based on method annotation.
This specific example shows annotation to mark a method that is only accessible by customer service. But unfortunately this isn't the only condition.
We have to confirm the customerServiceId that is also passed as one of method parameters. The parameter containing customerServiceId is pretty nested, so I was wondering if it's possible to get parameter value by some kind of a "path".
So let's say we have this method:
fun updateRemoteConfig(val remoteConfig: RemoteConfig) { doSomething() }
RemoteConfig class is pretty nested, so the path to customerServiceId would be something like: remoteConfig.customerService.id
What I would like to achieve is mark the method with annotation:
#CustomerServiceAccess(customerServiceIdPath = "remoteConfig.customerService.id")
And the value would then be fetched inside Aspect method. But I have no idea how to get to the specified value by path. Is it even possible?
The unknown is where arrows are in the code. Here's rest of the aspect:
#Aspect
class AuthorizationAspect {
#Pointcut("#annotation(com.my.project.annotations.CustomerServiceAccess)")
fun customerServiceAccess() = Unit
#Before("customerServiceAccess()")
fun checkAccess(joinPoint: JoinPoint) {
val methodSignature = joinPoint.signature as MethodSignature
val method = methodSignature.method
val canAccess = mutableListOf<() -> Boolean>()
.apply {
addAll(method.getAnnotationsByType(CustomerServiceAccess::class.java).map { it.canAccess(method) })
}
.any { it() }
if (!canAccess) {
throw UnauthorizedException(message = "User cannot perform this action")
}
}
private fun CustomerServiceAccess.canAccess(val method: Method): () -> Boolean = {
->> val customerServiceIdParam = method.getParameterByPath(getCustomerServiceIdPath())
SecurityContext.isCustomerService && SecurityContext.customerServiceId == customerServiceIdParam
}
private fun CustomerServiceAccess.getCustomerServiceIdPath(): String = this.customerServiceIdPath
}
#Retention(AnnotationRetention.RUNTIME)
#Target(AnnotationTarget.FUNCTION)
annotation class CustomerServiceAccess(val customerServiceIdPath: String)
Related
I have unit test in which I am trying to check is a use case is called with the right parameters but I get an error
java.lang.IllegalArgumentException: Parameter specified as non-null is null: method com.xx.xxx.clean.orderview.domain.OnStandUseCaseCoroutine$Params.<init>, parameter serviceType
#Test
fun `when notifyOnStand is called then we create a TimestampedAction with the correct user id, vehicle, timestamp and pass that to the usecase`() {
val actionCaptor = argumentCaptor<TimestampedAction>()
val timestamp = DateTime.now()
every(noServiceRequiredBus.get()).thenReturn(Observable.just(REQUESTED))
every(timingsUpdater.timestampCalculator(any(), any())).thenReturn(timestamp)
baseOrderViewPresenter.setView(view)
baseOrderViewPresenter.notifyOnStand()
runBlocking {
verify(onStandUseCaseCoroutine).run(OnStandUseCaseCoroutine.Params(any(), any(), capture(actionCaptor)))
}
}
Use case which will get called when when called baseOrderViewPresenter.notifyOnStand() from tets case
class OnStandUseCaseCoroutine #Inject constructor(
private val orderRepository: OrderRepository,
private val serviceOrderTypeProvider: ServiceOrderTypeProvider
) : UseCaseCoroutine<GenericResponse, OnStandUseCaseCoroutine.Params> (){
override suspend fun run(params: Params) =
orderRepository.notifyOnStandSuspend(serviceOrderTypeProvider.apiPathFor(params.serviceType), params.id, params.action)
data class Params(val serviceType: String, val id: String, val action: TimestampedAction)
}
Presenter layer which has the call to use case
private fun onstandUseCaseCoroutines(serviceType: String, id: String, action: TimestampedAction, callback: (GenericResponse?) -> Unit) {
try {
onStandUseCaseCoroutine(OnStandUseCaseCoroutine.Params(serviceType, id, action)) {
callback.invoke(it)
}
} catch (exception: Exception) {
onStandResponseErrors()
}
}
how can I fix this please
I tried changing to bellow code but that did not fix it, I am not sure what to do the capture(actionCaptor) bit if that is the issue
runBlocking {
verify(onStandUseCaseCoroutine).run(OnStandUseCaseCoroutine.Params(anyString(), anyString(), capture(actionCaptor)))
}
Any suggestions please
Thanks
R
I want to make a REST API call which return a boolean value as a part of the custom annotation.
Sample Code :
**#CustomAnnotation
public String myMethod(){
// my implementation
}**
Only if the boolean value from the REST call is true, the method "myMethod must get triggered and implementation should happen else throw an exception similar to #NotNull .
I was wondering if this is possible, if yes someone please help me out.
you can create a simple custom annotation without worrying about code to call rest.
For executing rest call code ->
Read about how to apply aspect oriented programming.
Basically using aop (apsect oriented programming), you can write a code such that for any method which is annotated with your custom annotation, you wish to execute some piece of code before calling your method.
To do this in spring
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#interface CustomAnnotation {
String value() default "";
}
#Pointcut(value = "#annotation(CustomAnnotation)") // full path to CustomAnnotation class
public void abc() {
}
#Around("abc()")
public Object executeSomePieceOfCode(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("this executes before calling method");
// YOUR CODE TO CALL REST API
boolean responseFromRestCall = true; // this flag is set based on response from rest call
if(responseFromRestCall) {
// this excutes your method
Object obj = joinPoint.proceed();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
CustomAnnotation myAnnotation = method.getAnnotation(CustomAnnotation.class);
String value = myAnnotation.value();
System.out.println("value : + " + value);
return obj;
} else {
// currently throwing RuntimeException. You can throw any other custom exception.
throw new RuntimeException();
}
}
I am trying to use Mockito on my mocked object in such a way that it should always return the very same object that was passed in as an argument. I tried it do to it like so:
private val dal = mockk<UserDal> {
Mockito.`when`(insert(any())).thenAnswer { doAnswer { i -> i.arguments[0] } }
}
However, this line always fails with:
io.mockk.MockKException: no answer found for: UserDal(#1).insert(null)
The insert(user: User) method doesn't take in null as an argument (obviously User is not a nullable type).
How can I make the insert() method always return the same object that it received as an argument?
When you're using MockK you should not use Mockito.
Only using MockK you can achieve the same with:
val dal = mockk<UserDal> {
every { insert(any()) } returnsArgument 0
}
If you intend to use Mockito, you should remove MockK and use mockito-kotlin:
val dal = mock<UserDal> {
on { insert(any()) } doAnswer { it.arguments[0] }
}
Given the following class (written in kotlin):
class Target {
fun <R> target(filter: String, mapper: (String) -> R): R = mapper(filter)
}
I'm able to test in java, the test code:
#Test
public void testInJava() {
Target mockTarget = Mockito.mock(Target.class);
Mockito.when(mockTarget.target(
argThat(it -> true),
Mockito.argThat(it -> true)
)).thenReturn(100);
assert mockTarget.target("Hello World", it -> 1) == 100;
}
The java test pass as expected, but the same test is written in kotlin:
#Test
fun test() {
val mockTarget = Mockito.mock(Target::class.java)
Mockito.`when`(mockTarget.target(
Mockito.argThat<String> { true },
mapper = Mockito.argThat<Function1<String, Int>>({ true }))
).thenReturn(100)
assert(mockTarget.target("Hello World") { 1 } == 100)
}
The kotlin version I receive the following exception:
java.lang.IllegalStateException: Mockito.argThat<String> { true } must not be null
Why is it happening and how can I test that using kotlin?
I also faced the same problem.
And finally, I found argThat() will return null, and normally the argument in the function in kotlin, does not accept null type.
The source code of argThat from ArgumentMatchers.java
public static <T> T argThat(ArgumentMatcher<T> matcher) {
reportMatcher(matcher);
return null;
}
You can see that it return null. So when we mock the function, it will throw IllegalStateException, because argThat returns null and argument can't be null.
It mean that if your function is:
fun doSomething(arg1: String): Int {
// do something
}
When you mock it like that:
Mockito.`when`(
doSomething(Mockito.argThat<String> { true })
).thenReturn(100)
It will throw IllegalStateException
So you should change your function like that:
fun doSomething(arg1: String?): Int {
// do something
}
Change the "String" to "String?", make it accept null type.
My solution is to define the argument with class? so that it can accept null, but I don't know if it is a great solution
In 2022, Mockito-Kotlin officially solves the problem.
The fix is very simple: Just import the argThat/eq/... from the mockito-kotlin package, instead of the mockito package, and everything is done!
Related: https://github.com/mockito/mockito-kotlin/wiki/Mocking-and-verifying
As of this writing, mockito-kotlin hasn't been updated for more than a year. As with all of these libraries, there's always a constant need for keeping them up-to-date, and I didn't want to get stuck with an unmaintained library.
So I came up with another way to solve the null issue with argThat without using any other libraries.
Say we've an interface UuidRepository as follows:
interface UuidRepository {
suspend fun Entity save(entity: Entity): Entity
}
class Entity has two properties, userId: String and uuid: String.
The following code fails:
Mockito.verify(uuidRepository).save(argThat { it.userId == someValue && it.uuid == "test" })
with the error:
argThat { it.userId == someValue && it.uuid == "test" } must not be null
To solve this, we get all the invocation on the mock and then verify the ones we want:
val invocations = Mockito.mockingDetails(uuidRepository).invocations
.filter { setOf("findById", "save").contains(it.method.name) }
.map { it.method.name to it.arguments }
.toMap()
assertThat(invocations).containsKey("save")
val first = invocations["save"]?.first()
assertThat(first).isNotNull
val entity = first as Entity
assertThat(entity.userId).isEqualTo(someValue)
assertThat(entity.uuid).isEqualTo("test")
I am trying to learn Kotlin and delegates are both interesting and confusing. I have a situation where, in a java class I would take a constructor arg, create a Future (the ID represents a resource in another system) and stash the Future as an instange variable. Then the "getXXX" would call Future.get()
Here is a sample java class
public class Example {
private Future<Foo> foo;
public Example(String fooId) {
this.foo = supplyAsync(() -> httpClient.get(fooId));
}
public Foo getFoo() {
return foo.get();
}
}
I am not supplying the Kotlin example because I am simply not sure how to construct it.
You can translate your Java code to Kotlin in a straightforward way using custom property getters:
class Example(fooId: Int) {
private val fooFuture = supplyAsync { httpClient.get(fooId) }
val foo: Foo
get() = fooFuture.get()
}
But Kotlin has a more powerful concept for generalizing property behavior -- the property delegates:
class Example {
val foo: Foo by someDelegate
}
In this example, someDelegate is an object that defines the behavior of property foo.
Though Future<V> cannot be used as a delegate out of the box in Kotlin, you can create your own property delegates by implementing getValue(thisRef, property) and (for mutable properties) setValue(thisRef, property, value) functions, thus explicitly providing the code to be executed when a property is read (and written, if mutable).
These functions can be either member functions for your project classes or extension functions, which fits the case with Future<V>. Basically, to use Future<V> as a property delegate, you have to define the getValue(thisRef, value) extension function for it, for example:
operator fun <V> Future<V>.getValue(thisRef: Any?, property: KProperty<*>) = get()
Here, the value the delegate will provide for a property will be simply taken from the Future::get call, but a proper implementation should probably take care of cancellation and exceptions handling. For this purpose, you can wrap a Future<V> into a class that will also define the fallback values/strategies, and then use this class' objects in by.
Then you can use Future<V> objects as delegates for your properties:
class Example(fooId: Int) {
val foo: Foo by supplyAsync { Thread.sleep(2000); fooId }
}
fun main(args: Array<String>) {
val e = Example(123)
println(e.foo)
}
To complete the answer from hotkey I propose this solution for a ReadWrite property
fun <V> Future<out V>.asDelegate(mayInterruptIfRunning: Boolean = false) =
object : ReadWriteProperty<Any?, V> {
private val lock = Object()
private var completedOrCanceled: Boolean = false
private var mValue: V? = null
override fun getValue(thisRef: Any?, property: KProperty<*>): V {
synchronized(lock) {
if (!completedOrCanceled) {
mValue = get()
completedOrCanceled = true
}
}
#Suppress("UNCHECKED_CAST")
return mValue as V
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: V) {
synchronized(lock) {
if (!completedOrCanceled) {
cancel(mayInterruptIfRunning)
completedOrCanceled = true
}
mValue = value
}
}
}
Usage could be :
class Example(fooId: Int) {
var foo: Foo by supplyAsync { Thread.sleep(2000); print("job done "); fooId }.asDelegate()
}
fun main(args: Array<String>) {
val e = Example(123)
println(e.foo) //"job done 123" printed after 2 sec
val f = Example(123)
f.foo = 456 //without waiting
println(e.foo) //"456" printed immediately
//"job done " printed after 2 sec
val g = Example(123) //"job done " printed after 2 sec
Thread.sleep(3000) //wait 3 sec then
println(e.foo) //"123" printed immediately
g.foo = 456
println(e.foo) //"456" printed immediately
}
note that if mayInterruptIfRunning is set to true then "job done " won't be printed if f.foo = 456 is called before the 2 sec