A test class for an interface, this test class has two http requests, so should I write two MOCKs? - apex

When I was writing a test class for an interface, I was told that the MOCK method needs to be used when the test class contains http requests.
Introduction of MOCK
Then I found that my interface needs to make two requests. The first time is to pass the account password to get the token, and then the second time I need to pass some content and the token and then return whether it is successful or not.
Both requests use the same method, but the input parameters are different
Then I made two requests in the code, so do I need to write two MOCK classes, or do I need to write two
Test.setMock(HttpCalloutMock.class, new YourHttpCalloutMockImpl());

You can use the same mock class and return two different HttpResponse instances depending on the HTTPRequest parameters or url path
// within the YourHttpCalloutMockImpl.response method add something like this
if(/* any criteria based on the request params or url path */) {
// return token response
} else {
// return second request
}

Related

Is there any way to access arguments of handler method in a NestJS guard?

I want to implement a smart guard in NestJS which can protect method execution based on decisions declared in a special method annotation.
For example the use-case is:
normal users can change only their own user information
Admin can change any user information
I want to specify decisions by declarative way: using annotations. I've almost completed the implementation, this requirement can be declared this way:
#Put('user')
#UseGuards(HttpBasicAuthGuard, DecisionGuard)
#DecisionExpr(new Decisions(Op.OR, [
new RootDecision(Role.ADMIN),
new CurrentUserDecision({ sourceParamId: 'user', func: (user: User) => user.id}),
]))
updateUser(#ParamId('user') #Body() u: User, #ParamId('etc') etc): any {
// call user service and update user in model
}
Declared decisions are stored into metadata of handler method by
'#DecisionExpr'.
Parameter indexes are collected by '#ParamId' and also stored into
metadata of handler method.
Decisions are evaluated by DecisionGuard which can access current
user and authorities from Request (via ExecutionContext), it can also
access easily decisions and identified parameter indexes from
metadata.
The only problem how can I access arguments of handler method from DecisionGuard. Arguments of handler method contain data which can be compared to the current user and make a decision for DecisionGuard.
Is it possible, at all?
The only way I found was to implement a method decorator and call a custom method by changing PropertyDescriptor.value (3rd argument of method decorator):
export const DecisionExpr = (data: Decision): MethodDecorator => {
...
const childFunction = descriptor.value;
descriptor.value = (...args: any[]) => {
console.log('DecisionExpr child function - args:', args);
// call original method
return childFunction.apply(this, args);
};
}
But the problem with this solution is that a custom method is only called AFTER guards.
My last chance is moving this protection from DecisionGuard into the custom method of DecisionExpr method decorator - but I don't like it. I don't want to introduce a new pattern for method protection.
Maybe I will try this solution to fulfill my requirements:
https://docs.nestjs.com/security/authorization#integrating-casl

Scala Dynamic Variable problem with Akka-Http asynchronous requests

My application is using Akka-Http to handle requests. I would like to somehow pass incoming extracted request context (fe. token) from routes down to other methods calls without explicitly passing them (because that would require modyfing a lot of code).
I tried using Dynamic Variable to handle this but it seems to not work as intended.
Example how I used and tested this:
object holding Dynamic Variable instance:
object TestDynamicContext {
val dynamicContext = new DynamicVariable[String]("")
}
routes wrapper for extracting and setting request context (token)
private def wrapper: Directive0 = {
Directive { routes => ctx =>
val newValue = UUID.randomUUID().toString
TestDynamicContext.dynamicContext.withValue(newValue) {
routes(())(ctx)
}
}
I expected to all calls of TestDynamicContext.dynamicContext.value for single request under my wrapper to return same value defined in said wrapper but thats not the case. I verified this by generating for each request separate UUID and passing it explicitly down the method calls - but for single request TestDynamicContext.dynamicContext.value sometimes returns different values.
I think its worth mentioning that some operations underneath use Futures and I though that this may be the issue but solution proposed in this thread did not solve this for me: https://stackoverflow.com/a/49256600/16511727.
If somebody has any suggestions how to handle this (not necessarily using Dynamic Variable) I would be very grateful.

Pass ID or Object which has irrelavant details as RequestBody in Rest Call?

Approach 1:
#PostMapping("/api/{id}")
String getSomeObj(int id){
//make another rest call with id and get CustomObj
// then do some logic and return something
//Here response time will be more as it has again another rest calls
}
Approach 2:
#PostMapping("/api/{id}")
String getSomeObj(#PathParam("id") int id, #RequestBody CustomObj obj){
//directly do logic with the provided obj and return something
//Here Response time would be less as we are directly getting the actual Object from Request Body
//BUT is this a good practise to pass an object in which we need only few details?
}
Q1) All I am asking is to whether to pass just id or Object? If id is passed, another Rest call has to be made unnecessarily. If Object is passed, we can avoid making another rest call, BUT the problem is: this custom object may contain some irrelavant details too.. So, is this correct?
Q2) If passed with id, response time will be more when comparing with just passing object.. So, I am not understanding which approach should follow..
A1) This is all up to you and there is no "one correct" way. I would say if it's a small object pass the object and respond fast. If its a big object pass the id. How do you define big and small objects? if object has hashmaps or lists in it that's a big object. Also you can ignore serialization of internals; check https://www.baeldung.com/jackson-ignore-properties-on-serialization
A2) Pass the id and enjoy your REST service. After all REST is very fast. Don't worry about the speed of calls. If your back end function is fast and if you put a "loading" gif to front end; users will wait for the response.

download a file with play web api (async)

I am trying to download a file using play api framework. Since all the data access layer has already been implemented with Futures I would like to get download to work with async action as well. However, the following piece of code does not work. And by not working I mean that the file sent to the client is not the same as the file on server.
val sourcePath = "/tmp/sample.pdf"
def downloadAsync = Action.async {
Future.successful(Ok.sendFile(new java.io.File(sourcePath)))
}
However, this piece works:
def download = Action {
Ok.sendFile(new java.io.File(sourcePath))
}
Any suggestion on how I can get the async method to work?
You actually don't need to use Action.async here, since Ok.sendFile is non-blocking already. From the docs:
Play actions are asynchronous by default. For instance, in the controller code below, the { Ok(...) } part of the code is not the method body of the controller. It is an anonymous function that is being passed to the Action object’s apply method, which creates an object of type Action. Internally, the anonymous function that you wrote will be called and its result will be enclosed in a Future.
def echo = Action { request =>
Ok("Got request [" + request + "]")
}
Note: Both Action.apply and Action.async create Action objects that are handled internally in the same way. There is a single kind of Action, which is asynchronous, and not two kinds (a synchronous one and an asynchronous one). The .async builder is just a facility to simplify creating actions based on APIs that return a Future, which makes it easier to write non-blocking code.
In other words, at this specific case, don't worry about wrapping your Result into a Future and just return Ok.sendFile.
Finally, both versions works as expected to me (the file was properly delivered). Maybe you are having another problem not related to how you have declared your actions.

Logging user activity in another logical layer without global HttpContext

I'm looking for a good solution to log DB changes in a web application developed using Play/Scala/ReactiveMongo. I need to know who changed what.
I have a separate layer namely Services in which all data access and business logics happens. All saves/updates/removes are done by certain methods so I can log them safely in just 3 or 4 methods but I need user identity there.
I don't have access to current user in services! There is no global HttpContext or Request or something like that which let me get the user identity (I think this way of getting user identity was incorrect of course).
I have a solution:
Add an implicit parameter to all methods of services which have side effects (change DB) and pass user identity to them.
def save(model: A)(implicit userIdentity: Option[UserIndentity] = None) = { ... }
As I wrapped default request, it can extend UserIdentity trait so that the implicit request matches the implicit parameters.
class MyRequest[A](...) extends WrappedRequest[A](request) extends UserIdentity
Finally, actions can use services like this:
def index() = MyAction { implicit request =>
//...
defaultService.save(model)
//...
}
The bad thing is that I have to add implicit parameters to those service methods. Isn't there another solution to get current user without polluting method signatures.
What's the problem with simply adding UserIdentity as an argument to your functions? Knowing who the user is seems to be important for your business logic - after all, today you want to log who performed the operation, tomorrow you will want to make sure this particular user is allowed to do it.
And I would just use a real UserIdentity object, not some hack with WrappedRequest, your services don't need to mess with a WrappedRequest instance.