How to implement volleyresponse listener using Kotlin - kotlin

I am trying to move my volley requests into a class, so I can use it for multiple network calls. I need a way to access the response listener in whatever activity I use in this class. i saw some examples in java, But I am finding it difficult to do achieve this.
import android.content.Context
import com.android.volley.DefaultRetryPolicy
import com.android.volley.Request
import com.android.volley.RequestQueue
import com.android.volley.Response
import com.android.volley.toolbox.StringRequest
import com.android.volley.toolbox.Volley
interface VolleyResponse{
}
class NetworkCall(LINK:String,
CONTEXT:Context,
CACHE:Boolean,
PARAMS: HashMap<String, String> = HashMap(),
SuccessListener: Response.Listener<String>,
ErrorListener: Response.ErrorListener ) {
private var link:String = LINK
private var context: Context = CONTEXT
var cache: Boolean = CACHE
var PARAMS: HashMap<String,String> = HashMap()
fun RunTask( ){
//BUILD the request and listen for error or success
var request = object : StringRequest(
Request.Method.POST,link,
Response.Listener { response -> { }
},
Response.ErrorListener { error -> { }
}) {
override fun getParams(): HashMap<String, String> {
return PARAMS
}
}
var RequestQueue: RequestQueue = Volley.newRequestQueue(context)
request.setShouldCache(cache)
request.setRetryPolicy(DefaultRetryPolicy(10000, 0, 0F))
}
}
and i call it like this...
fun processLogin() {
var params:HashMap<String,String> = HashMap()
params.put("user_email","username")
params.put("user_password","password")
var networkCall = NetworkCall("",applicationContext,false,params)
}
I just need to be able to access the response listeners in my processLogin function.

First you have to define implementations of Response.Listener<String> and Response.ErrorListener in the class where processLogin is defined, this can be done as following
private val successListener = Response.Listener<String> {
// Do something when response is received
}
private val errorListener = Response.ErrorListener {
// Do something when error is received
}
now pass these as parameters when you call processLogin as following
var networkCall = NetworkCall("",applicationContext,false,params, successListener, errorListener)
Finally you need to update your NetworkCall class so that these listeners are called on network action
fun RunTask( ){
//BUILD the request and listen for error or success
var request = object : StringRequest(
Request.Method.POST,link,
SuccessListener, // Pass listeners to request
ErrorListener) {
override fun getParams(): HashMap<String, String> {
return PARAMS
}
}

Related

What is the best way to get data from an API using retrofit when something needs to wait on that data?

I have a retrofit API call but am having trouble getting the data out of it:
This is in a class file that's not a viewModel or Fragment. It's called from the apps main activity view model. I need to be able to get the data from the API and wait for some processing to be done on it before returning the value back the view model. Newer to kotlin and struggling with all the watchers and async functions. The result of this an empty string is the app crashes, because it's trying to access data before it has a value.
From class getData which is not a fragment
private lateinit var data: Data
fun sync(refresh: Boolean = false): List<String> {
var info = emptyList<String>
try {
getData(::processData, ::onFailure)
info = data.info
} catch(e: Throwable){
throw Exception("failed to get data")
}
}
}
return info
}
fun getData(
onSuccess: KFunction1<ApiResponse>?, Unit>,
onFailed: KFunction1<Throwable, Unit>
) {
val client = ApiClient().create(Service.RequestData)
val request = client.getData()
request.enqueue(object : Callback<ApiResponse> {
override fun onResponse(
call: Call<ApiResponse>,
response: Response<ApiResponse>
) {
onSuccess(response.body())
}
override fun onFailure(call: Call<RegistryResponse<GlobalLanguagePack>>, t: Throwable) {
onFailed(Exception("failed to get data"))
}
})
}
private fun processData(body: ApiResponse?) {
requireNotNull(body)
data = body.data
}
```
From appViewModel.kt:
```
fun setUpStuff(context: Context, resources: AppResources) = viewModelScope.launch {
val stuff = try {
getData.sync()
} catch (e: Exception) {
return#launch
}
if (stuff.isEmpty()) return#launch
}
```

Test case is calling actual method even after mocking the method call in ktor framework (kotlin)

I am testing an API written in Kotlin using the KTOR framework. For the testing, I am using JUnit5 and Mockito. There is a route class where a route is defined which I need to test. Here is the route class :-
fun Application.configureRouting() {
routing {
post("/someRoute") {
val service = MyService()
val request: JsonNode = call.receive()
launch {
service.dummyFunction(request)
}
val mapper = ObjectMapper()
val responseStr = "{\"status\":\"success\",\"message\":\"Request has been received successfully\"}"
val response: JsonNode = mapper.readTree(responseStr)
call.fireHttpResponse(HttpStatusCode.OK, response)
}
}
}
This is the test case I am writing for it :-
class RouteTest {
#Mock
var service = MyService()
// read the configuration properties
private val testEnv = createTestEnvironment {
config = HoconApplicationConfig(ConfigFactory.load("application.conf"))
}
#Before
fun setUp() = withApplication(testEnv) {
MockitoAnnotations.openMocks(MyService::class)
}
#Test
fun test() = withApplication(testEnv) {
withTestApplication(Application::configureRouting) {
runBlocking {
Mockito.`when`(service.dummyFunction(Mockito.any()).thenReturn(true)
with(handleRequest(HttpMethod.Post, "/someRoute") {
setBody("some body")
}) {
assertEquals(HttpStatusCode.OK, response.status())
}
}
}
}
}
When I run the test, it calls the actual "dummyFunction()" method instead of the mocked one and hence, it is failing. Am I doing something wrong?
Because your service in test is different from the service you mocked. To solve this, you need to inject the service into your class, or pass the service as an argument.
Read more: IoC, DI.
The simplest way to solve your problem is to define the service parameter for the configureRouting method and pass a corresponding argument in the test and production code when calling it.
fun Application.configureRouting(service: MyService) {
routing {
post("/someRoute") {
val request: JsonNode = call.receive()
launch {
service.dummyFunction(request)
}
val mapper = ObjectMapper()
val responseStr = "{\"status\":\"success\",\"message\":\"Request has been received successfully\"}"
val response: JsonNode = mapper.readTree(responseStr)
call.fireHttpResponse(HttpStatusCode.OK, response)
}
}
}
class RouteTest {
#Mock
var service = MyService()
private val testEnv = createTestEnvironment {
config = HoconApplicationConfig(ConfigFactory.load("application.conf"))
}
#Test
fun test() = withApplication(testEnv) {
withTestApplication({ configureRouting(service) }) {
runBlocking {
// Your test...
}
}

Testing Soap Consumer. 'uri' must not be empty

I have a method for calling the "marshalSendAndReceive" method on sending a message over Soap.
class SoapConnector : WebServiceGatewaySupport() {
fun getClient(documentId: String): Person {
val request = getSearchRequest(documentId)
val response = webServiceTemplate.marshalSendAndReceive(request) as SearchResponse
return getPerson(response)
}
I also have a configuration file.
#Configuration
class SearchClientConfig {
#Bean
fun marshaller(): Jaxb2Marshaller? {
return Jaxb2Marshaller().apply {
contextPath = "wsdl" //here path to generated java classes from wsdl
}
}
#Bean
fun searchCilent(marshallerJaxb: Jaxb2Marshaller): SoapConnector {
return SoapConnector().apply {
defaultUri = "http://20.40.59.1:8080/cdi/soap/services/ServiceWS"
marshaller = marshallerJaxb
unmarshaller = marshallerJaxb
}
}
}
I want to Mock this method.
#RunWith(SpringRunner::class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#Sql(scripts = ["classpath:db/init.sql"])
#AutoConfigureEmbeddedDatabase(
beanName = "dataSource",
provider = AutoConfigureEmbeddedDatabase.DatabaseProvider.DOCKER
)
class SoapTest : WebServiceGatewaySupport(){
private lateinit var webServiceGatewaySupport: WebServiceGatewaySupport
private lateinit var connector: SoapConnector
#BeforeEach
fun setUp() {
webServiceGatewaySupport = Mockito.mock(WebServiceGatewaySupport::class.java)
}
#Test
fun getClientTest() {
Mockito.`when`(webServiceTemplate.marshalSendAndReceive(ArgumentMatchers.anyObject())).thenReturn(SearchResponse())
connector.getClient(RandomStringUtils.randomAlphabetic(7))
}
}
But there is a bug:
java.lang.IllegalArgumentException: 'uri' must not be empty
I don't understand what the problem is and I don't know how to make a working Mock of this method.

How to get lambda/function signature in Kotlin?

I have developed the code below:
import com.google.gson.Gson
import com.rabbitmq.client.Delivery
typealias MessageHandler<T> = (payload: T, args: HashMap<String, String>) -> Unit
class MessageRouter {
private var handlers: HashMap<String, Function<Unit>> = hashMapOf()
fun <T> bind(routingKey: String, handler: MessageHandler<T>) : MessageRouter {
handlers[routingKey] = handler
return this
}
fun consume(message: Delivery) {
if(message.envelope.routingKey in handlers) {
val currentHandler = handlers[message.envelope.routingKey]
if(message.properties.contentType == "application/json") {
///// TODO: Get function signature!
val gson = Gson()
//// TODO: Deserialize data and call the handler function
}
}
}
}
To be used as follows:
val messageRouter = MessageRouter()
val messageRouter = MessageRouter()
messageRouter.bind(EventSubscriberConstants.REGISTRATION_ROUTING_KEY) { user: UserDTO, _ -> register(user) }
basicConsume(
queueName, true,
{ _, message ->
messageRouter.consume(message)
}, null, null
)
I need the input lambda signature to extract UserDTO class and use Gson to deserialize it and process. I can not find any way to get function signature in Kotlin!

LiveData observation won't update

I want to send some data to the server and show its response to the user.
I'm using MVVM so I created a repository like this:
class Repository {
fun getData(context: Context, word: String): LiveData<String> {
val result = MutableLiveData<String>()
val request = object : StringRequest(
Method.POST,
"https://.......",
Response.Listener {
result.value = it.toString()
},
Response.ErrorListener {
result.value = it.toString()
}) {
#Throws(AuthFailureError::class)
override fun getParams(): MutableMap<String, String> {
val params = HashMap<String, String>()
params["word"] = word
return params
}
}
val queue = Volley.newRequestQueue(context)
queue.add(request)
return result
}
}
which just sends 'word' to the server and get its response.
my view model class contains just a mutableLiveData and a function. it is like this:
class ViewModel(application: Application) : AndroidViewModel(application) {
var result = MutableLiveData<String>()
fun getData(word: String): LiveData<String> {
val repository = Repository()
result = repository.getData(getApplication(), word) as MutableLiveData<String>
return result
}
}
I set an observation for result in my main Activity, therefore it is my MainActivity codes:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val model = ViewModelProviders.of(this).get(ViewModel::class.java)
submit.setOnClickListener {
model.getData(search_txt.text.toString())
}
model.result.observe(this, Observer {
Log.i("Log", "observe is :$it")
text.text = it
})
}
but it doesn't work! I get the user's input using an edit text and after pressing a button, I call getData function which is in my View Model class. but it returns always null and observation won't work.
I try to put observe the method in my button listener, in this way I get the result but it seems it's not a correct way because after I rotate my phone, all data ware gone and I need to fetch data from the server again while it shouldn't.