Kotlin Retrofit Post Request some posted values shows empty result in Database - android

So I have an API endpoint (https://someEndpoint.com/someEndpoint/e-member?insert) with a body looks like below as shown in Postman:
{
"emember_id": "B3456",
"emember_name": "Darren Kent",
"emember_gender" : "L",
"emember_nohp" : "08123456799",
"emember_email" : "darreen#gmail.com",
"emember_address" : "Kost Mawar",
"emember_username" : "Dreen12",
"emember_password" : "Rahasia123456",
"emember_dob" : "1996-03-13T00:00:00.000Z",
"emember_lastupdate" : "0000-00-00T00:00:00.000Z"
}
And this with this Retrofit Client
object RetrofitClient {
private const val BASE_URL = "https://someEndpoint.com/someEndpoint/e-member?insert"
val instance: API by lazy{
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
retrofit.create(API::class.java)
}
}
With Interface:
interface API {
#POST("e-member?insert")
fun insertNewMember(#Body postedInformation : RegisterMemberRequestBody): Call<DefaultResponse>
}
And this is the Data class RegisterMemberRequestBody :
#Parcelize
data class RegisterMemberRequestBody(
#field:SerializedName("emember_id")
val ememberId: String? = null,
#field:SerializedName("emember_username")
val ememberUsername: String? = null,
#field:SerializedName("emember_gender")
val ememberGender: String? = null,
#field:SerializedName("emember_name")
val ememberName: String? = null,
#field:SerializedName("emember_password")
val ememberPassword: String? = null,
#field:SerializedName("emember_dob")
val ememberDob: String? = null,
#field:SerializedName("emember_nohp")
val ememberNohp: String? = null,
#field:SerializedName("emember_email")
val ememberEmail: String? = null,
#field:SerializedName("emember_lastupdate")
val ememberLastupdate: String? = null,
#field:SerializedName("emember_address")
val ememberAddress: String? = null
) : Parcelable
And this is the snippet from the fragment where I call the Retrofit Client
RetrofitClient.instance.insertNewMember(RegisterMemberRequestBody(
memberId,
username,
selectedGender,
fullName,
password,
selectedBirthday,
phoneNumber,
email,
lastUpdate,
address
)).enqueue(object: Callback<DefaultResponse> {
override fun onFailure(call: Call<DefaultResponse>, t: Throwable) {
Toast.makeText(activity as AppCompatActivity, "Request Failed!", Toast.LENGTH_LONG).show()
binding.registerFormRelativeLayout.visibility = View.VISIBLE
binding.progressBar.visibility = View.GONE
}
override fun onResponse(
call: Call<DefaultResponse>,
response: Response<DefaultResponse>
) {
Log.i("register", response.body().toString())
//renderSnackBar()
navigateToLoginPage()
}
})
The parameters (memberId,username ,selectedGender ,fullName ,password ,selectedBirthday ,phoneNumber ,email ,lastUpdate ,and address) for the Data class is picked from some EditTexts. These are the inputted value as I printed it in the LogCat :
However, When the function executed it shows in the Database (Firebase) as :
As you can see that some values are not inserted to the database. Any Idea how does this happen ? Is the anything I need to change ? If I miss to point out any detail feel free to ask.
Note: the fullname, address, phoneNumber, selectedBirthDay, and selectedGender is passed from different Fragment. But I'm sure 100% that there's nothing wrong with it since it shows up the desired value when I printed it in the current fragment.
**Edit: **
This is how the value fullname, address, phoneNumber, selectedBirthDay, and selectedGender are passed from other fragment to this fragment. This is the other snippet from the current fragment where the values are accepted
lateinit var binding: FragmentRegisterPart2Binding
lateinit var fullName: String
lateinit var selectedGender: String
lateinit var selectedBirthday: String
lateinit var phoneNumber: String
lateinit var address: String
lateinit var username: String
lateinit var password: String
lateinit var email: String
lateinit var confirmPassword: String
var memberId: String = ""
var lastUpdate: String = "0000-00-00T00:00:00.000Z"
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(
inflater,
R.layout.fragment_register_part2, container, false
)
val args = arguments?.let {
RegisterFragmentPart2Args.fromBundle(
it
)
}
if (args != null) {
fullName = args.fullName
address = args.address
phoneNumber = args.phoneNumber
selectedBirthday = args.selectedBirthday
selectedGender = args.selectedGender
}
binding.finishRegistrationButton.setOnClickListener {
validateInformationInput()
}
binding.progressBar.visibility = View.GONE
return binding.root
}
Edit
Things that I've tried are including :
Make sure the Retrofit Client Object does not have any problem since it successfully sent the data to the server
The passed values from the other fragment have no problem since it's showed up properly in the current fragment
The Interface has no problem since it sent some value successfully
The Data class structure has no problem since it successfully sent some value as well.
Now I'm wondering whether some attributes of the data class are not properly delcared or something like that. (I'm using an extension in Android studio that automatically convert the Copied Json body into a proper data class)

This happens because you created data class with some fields from EditTexts, when you do that others are set to null because you have put default value to be null. Also you have put all val on all fields, so when you create object with some values and some nulls you cant change those later from api

Related

OMDb Api doesn't show any result - Kotlin, Android Studio

I followed everything carefully, step by step, but I can't find what the problem is.
Whenever I fill in the name of the movie I intend to search by, the app crashes and it says this:
java.lang.NullPointerException: response.body() must not be null
Does anyone have any idea? Here is the following code.
MovieList.kt
class MovieList {
val imdbID: String
val Title: String
val Year: String
val Plot: String
val Poster: String
constructor(imdbID: String, Title: String, Year: String, Plot: String, Poster: String) {
this.imdbID = imdbID
this.Title = Title
this.Year = Year
this.Plot = Plot
this.Poster = Poster
}
}
OMDbApi.kt
interface OMDbApi {
#GET("t={Title}")
fun getMovieByTitle(#Path("Title") Title: String): Call<MovieList>
}
OMDbApiClient.kt
class OMDbApiClient {
companion object{
private var omdbapi: OMDbApi? = null
fun getOMDbApi(): OMDbApi?{
if(omdbapi == null){
omdbapi = Retrofit.Builder()
.baseUrl("http://www.omdbapi.com/?&apikey=eb73867f")
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(OMDbApi::class.java);
}
return omdbapi
}
}
}
FirstFragment.kt
class FirstFragment : Fragment() {
private lateinit var omdbapiClient: OMDbApi
private lateinit var tvMovieTitle: TextView
private lateinit var ivImagePoster: ImageView
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_first, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
omdbapiClient = OMDbApiClient.getOMDbApi()!!
val movieId: EditText = view.findViewById<EditText>(R.id.TitleId)
tvMovieTitle = view.findViewById(R.id.MovieTitleId)
ivImagePoster = view.findViewById(R.id.MoviePosterId)
movieId.setOnEditorActionListener { v, actionId, event ->
if(actionId == EditorInfo.IME_ACTION_DONE || actionId == EditorInfo.IME_ACTION_NEXT){
val movietitle: String = movieId.text.toString()
searchMovieByTitle(movietitle)
true
}
else{
Toast.makeText(activity, "Error!", Toast.LENGTH_LONG).show()
false
}
}
}
private fun searchMovieByTitle(movietitle: String) {
omdbapiClient.getMovieByTitle(movietitle).enqueue(object : Callback<MovieList>{
override fun onResponse(call: Call<MovieList>?, response: Response<MovieList>) {
displayData(response.body())
Toast.makeText(activity, "Success!", Toast.LENGTH_LONG).show()
}
override fun onFailure(call: Call<MovieList>?, t: Throwable?) {
Toast.makeText(activity, "Error!", Toast.LENGTH_LONG).show()
}
})
}
private fun displayData(data: MovieList) {
tvMovieTitle.text = data.Title
Glide.with(this).load(data.Poster).into(ivImagePoster)
}
}
I think you are getting an error response and that's why the response.body() is null. You should be handling an error response gracefully, instead of expecting the body to always not be null, but that is another matter.
I think the following lines of code are at fault:
interface OMDbApi {
#GET("t={Title}")
fun getMovieByTitle(#Path("Title") Title: String): Call<MovieList>
}
The 'Title' should be a #Query parameter instead of a #Path parameter, because the &t= part in the API url is a query parameter. When it is a query parameter you also don't specify it as part of the path in #GET.
Your code would then become:
interface OMDbApi {
#GET("")
fun getMovieByTitle(#Query("t") title: String): Call<MovieList>
}
Do this change and see if you still get an error response. You might want to check the log in the 'Logcat' tab to see what error you are getting back from the API.
For that you might have to enable a higher logging level. This is done by adding a logging interceptor when building the Retrofit client.
// Build a http client with logging enabled
val client = OkHttpClient.Builder()
.addInterceptor(HttpLoggingInterceptor().apply {
// you can also use Level.BODY for even more log information
level = HttpLoggingInterceptor.Level.BASIC
})
.build()
omdbapi = Retrofit.Builder()
.client(client) // add this line to use your http client
.baseUrl("http://www.omdbapi.com/?&apikey=eb73867f")
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(OMDbApi::class.java);
The following code solved my problem.
interface OMDbApi {
#GET("?apikey=[your api key]&")
fun getMovieByTitle(#Query("t") Title: String): Call<MovieList>
}

why is the key not added to the data in firebase?

I've been struggling with the problem for a long time and I can't solve it.I'm in EditActivity trying to add a key to the data from the firebase that I get from it with the help of Ad and process it using the AdsManager.
The key should not be equal to zero. In this line I check if the key is zero, then the Firebase writes data to empty.db.child (ad.key?: "Empty"). SetValue (ad). When I load the data to the Firebase, it shows them empty. Without displaying the key. You know what the problem is?
Ad
data class Ad(
var user_id : String? = null ,
var name : String? = null ,
var button1 : String? = null ,
val textTex : String? = null ,
val textk : String? = null ,
val description : String? = null,
val name_ads: String?=null,
val key :String? =null
)
AdsManager
class AdsManager(val readDataColbak : ReadDataColbak?) {
private lateinit var auth: FirebaseAuth
val db =Firebase.database.getReference("main")
fun pubilshAd(ad :Ad) {
db.child(ad.key?: "empty").setValue(ad)
}
fun ReadDataDb() {
db.addListenerForSingleValueEvent(object :ValueEventListener{
override fun onDataChange(snapshot : DataSnapshot) {
val adArray =ArrayList<Ad>()
for (item in snapshot.children){
val ad =item.children.iterator().next().child("ad").getValue(Ad::class.java)
adArray.add(ad !!)
}
readDataColbak?.readData(adArray)
}
override fun onCancelled(error : DatabaseError) {
}
})
}
}
EditActivity1
class EditActivity1: AppCompatActivity(), FfragmentInterfes{
lateinit var mBinder : ActivityEdit1Binding
private var dialog=D()
private var chooseImageFrog:FragmentList? =null
val adsManager = AdsManager(null)
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
imageAdapter = ImageAdapter()
mBinder=ActivityEdit1Binding.inflate(layoutInflater)
setContentView(mBinder.root)
init()
}
fun onClickPublish(view : View) {
adsManager.pubilshAd(fillAd())
}
private fun fillAd() : Ad {
var ad : Ad
mBinder.apply {
ad= Ad(
name_ads.text.toString(),
user_id.text.toString(),
name.text.toString(),
button1.text.toString(),
description.text.toString(),
textk.text.toString(),
adsManager.db.push().key
)
}
return ad
}

Retrofit response.body is null while using league of legends API

I try to recover the data of a player with the league of legends API however the response to my request is always null and those without an error message in my logcat.
here is my retrofit call:
public interface LolApiService {
#GET("summoners/by-name/")
Call<SummonerData> getSummonerData (#Query("summonerName")String summonerName, #Query("key") String key);
}
here is my repository:
class LolApiRepository(val application: Application) {
val response = MutableLiveData<SummonerData>()
fun getSummonerID(summonerName: String, key: String): MutableLiveData<SummonerData> {
// val responseData = MutableLiveData<SummonerData>()
val retrofit = Retrofit.Builder()
.baseUrl("https://euw1.api.riotgames.com/lol/summoner/v4/")
.addConverterFactory(GsonConverterFactory.create())
.build()
val service = retrofit.create(LolApiService::class.java)
service.getSummonerData(summonerName, key).enqueue(object : Callback<SummonerData> {
override fun onFailure(call: Call<SummonerData>, t: Throwable) {
Toast.makeText(application, "Error wile accessing the API", Toast.LENGTH_SHORT)
.show()
}
override fun onResponse(call: Call<SummonerData>, resp: Response<SummonerData>) {
Log.d("LolApiRepository", "LolApiRepository:" + resp.body().toString())
if (resp.body() != null) {
Toast.makeText(application, "Success accessing the API", Toast.LENGTH_SHORT)
.show()
response.value = (resp.body() as SummonerData)
} else {
Log.d("LolApiRepository", "LolApiRepository:" + resp.errorBody().toString())
Toast.makeText(application, "Error wile accessing the API", Toast.LENGTH_SHORT)
.show()
}
}
})
return response
}
}
my data model in which I retrieve the result of my query:
class SummonerData {
#SerializedName("id")
#Expose
var id: String? = null
#SerializedName("accountId")
#Expose
var accountId: String? = null
#SerializedName("puuid")
#Expose
var puuid: String? = null
#SerializedName("name")
#Expose
var name: String? = null
#SerializedName("profileIconId")
#Expose
var profileIconId: Int? = null
#SerializedName("revisionDate")
#Expose
var revisionDate: Int? = null
#SerializedName("summonerLevel")
#Expose
var summonerLevel: Int? = null
}
the fragment in which I want to display the data:
class LolStatFragment : Fragment() {
private lateinit var mViewModel: LolApiViewModel
private val apiKey = "api_key=RGAPI-bb27988b-cbb1-4767-b18b-icar8e90c308"
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_lol_stat, container, false)
mViewModel = ViewModelProviders.of(this).get(LolApiViewModel::class.java)
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
summoner_search.setOnClickListener {
val summonerName = summoner_name.text.toString()
mViewModel.summonerIds(summonerName,apiKey).observe(viewLifecycleOwner,Observer<SummonerData>{
summoner_ID.text = it.id
Log.d("LolStatFragment", "LolStatFragment:" + it.id)
Toast.makeText(context, "zzzzzzzzz ${it.id}", Toast.LENGTH_SHORT).show()
})
}
}
}
here is the result of my retrofit request on a web browser:
{"id":"OR5-q4c9Mw3jKXcPZw2lXul0tT7eLf4dYNadYrGhQ9mG8-w","accountId":"gOb2ZjN51iRLnRmDJuR5GmfILqP3x-T3qfbKWaTZ9k3dYw","puuid":"9TgzR6qdI_X9Z6xFzV0nFndITN0LSGKKeJ5fol2Ii1a01l4duKvFwpYGJQvBeYkBLkvJc96Sr7DZMg","name":"Practice","profileIconId":4353,"revisionDate":1619525378251,"summonerLevel":209}
thank you to all those who will take the time to answer me !
PS:this is my first question on the forum, I hope to have been clear and to have asked my question correctly,If there's any detail that I left out for this question, feel free to ask.
I finally found the answer to my problem, the url of the network call was not formatted well. here is the code used to retrieve my call url api in my OnResponse method and compare it to that of the browser:
Log.d("LolApiRepository", "LolApiRepository:" + resp.toString())
this is what I had to change in my LolApiService interface :
public interface LolApiService {
#GET("summoners/by-name/{summonerName}?")
Call<SummonerData> getSummonerData (#Path("summonerName") String summonerName, #Query("api_key") String key);
}

Android Kotlin Retrofit Post Request Inputted data does not sent

So I have to handle a POST request with its body data taken from some input in a form.
The Endpoint of this service is https://someUrl.com/switching-product/switch?orderID=A002&procode=0200011&nip=P19120
The Response returned from Postman is like this.
The body of this request is like this :
In this case, I have this Interface for handling a POST request:
///Endpoint: https://someUrl.com/switching-product/switch?orderID=A002&procode=0200011&nip=P19120
interface editProductDetail{
#FormUrlEncoded
#POST("switch")
fun editProductDetail(#Query("orderID") orderID: String,
#Query("procode") procode: String,
#Query("nip") nip : String,
#Field("procode_new") procodeNew: String,
#Field("item_qty_new") itemQtyNew: String,
#Field("item_price_new") itemPriceNew: String,
#Field("item_name_new") itemNameNew: String,
#Field("total_price_new") totalPriceNew: String): Call<OutstandingOrderDetailPOJODataClassDetailItem>
}
This is the data class I use:
data class OutstandingOrderDetailPOJODataClassDetailItem(
#field:SerializedName("item_price_new")
val itemPriceNew: Int? = null,
#field:SerializedName("item_name_new")
val itemNameNew: String? = null,
#field:SerializedName("total_price")
val totalPrice: Int? = null,
#field:SerializedName("item_price")
val itemPrice: Int? = null,
#field:SerializedName("item_name")
val itemName: String? = null,
#field:SerializedName("status_refund")
val statusRefund: String? = null,
#field:SerializedName("detail_id")
val detailId: Int? = null,
#field:SerializedName("procode_new")
val procodeNew: String? = null,
#field:SerializedName("refund_date")
val refundDate: String? = null,
#field:SerializedName("request_refund")
val requestRefund: String? = null,
#field:SerializedName("procode")
val procode: String? = null,
#field:SerializedName("last_update")
val lastUpdate: String? = null,
#field:SerializedName("item_qty_new")
val itemQtyNew: Int? = null,
#field:SerializedName("order_id")
val orderId: String? = null,
#field:SerializedName("total_price_new")
val totalPriceNew: Int? = null,
#field:SerializedName("item_qty")
val itemQty: Int? = null,
#field:SerializedName("refund")
val refund: Int? = null
)
As you can see above, the body has attributes that are included in the data class as well. (This data class is also used in a related service in the App that uses the inputted data of this service).
And this is the function to trigger the POST request:
NetworkConfig().editOutstandingOrderProductDetailService().editProductDetail(
selectedOrderId,
selectedProcode,
selectedNip,
procodeNew,
itemNewQty,
itemPriceNew,
itemNewName,
totalPriceNew
).enqueue(object :
Callback<OutstandingOrderDetailPOJODataClassDetailItem> {
override fun onFailure(call: Call<OutstandingOrderDetailPOJODataClassDetailItem>, t: Throwable) {
Log.i("Order", "It Failed!!")
if (call.isCanceled) {
Toast.makeText((activity as AppCompatActivity), "Request Aborted", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText((activity as AppCompatActivity), t.localizedMessage, Toast.LENGTH_SHORT).show()
}
}
override fun onResponse(
call: Call<OutstandingOrderDetailPOJODataClassDetailItem>,
response: Response<OutstandingOrderDetailPOJODataClassDetailItem>
) {
Log.i("Order", "Switching Process done!!!")
Log.i("Order", "Status: ${response.body()}")
}
})
From above, it prints the response in the logCat like this:
Am I missing something here? Or there's something I need to Change? If there's any detail that I missed to point out, just let me know!
Your request is a JSON object, not a formurl.
#Field tag is used when you want to pass your parameters as formurl
Use model class or JsonObject with #Body tag to pass parameters as JsonObject.
Model class Exmaple,
data class TestClass{
val text1: String,
val text2: String
}
Now Pass this class as request
#POST("URL")
fun apiName(#Body request: TestClass);
JSON Object Exmaple,
JSONObject paramObject = new JSONObject();
paramObject.put("key1", "value1");
paramObject.put("key1", "vaalue2");
Now pass it as String or JsonObject
#POST("URL")
fun apiName(#Body request: String); // pass as String

How to get a document contents from Firestore by custom object? (Android - Kotlin)

I'm trying to get a document contents from Firestore. The following image link shows the database structure Firestore database structure
What I want: I want to get a document contents by a custom object and add the contents to a list.
The problem: I'm getting this error:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.aalmesbah.turoodpilot, PID: 12160
java.lang.RuntimeException: Could not deserialize object. Class com.google.firebase.auth.UserInfo does not define a no-argument constructor. If you are using ProGuard, make sure these constructors are not stripped
I tried to get the document content by get() and getString() methods and it worked fine the problem is only with the toObject()?
I've searched and tried some suggested solutions from other questions here like add default values for the data class, but it didn't work, unfortunately.
data class code:
data class UserInfo (val name: String? = "",
val email: String? = "",
val phoneNum: String? = "",
val address: String? = "") {
constructor(): this("","","", "" )
}
Profile Fragment code: (where the document contents suppose to be shown)
class ProfileFragment : Fragment() {
private lateinit var auth: FirebaseAuth
private lateinit var db: FirebaseFirestore
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_profile, container, false)
auth = FirebaseAuth.getInstance()
db = FirebaseFirestore.getInstance()
return view
}
override fun onStart() {
super.onStart()
val userID = auth.currentUser?.uid
val docRef = db.collection("users").document(userID!!)
docRef.addSnapshotListener(EventListener<DocumentSnapshot> { documentSnapshot, e ->
if (e != null) {
Log.w(TAG, "Listen failed.", e)
return#EventListener
}
if (documentSnapshot != null && documentSnapshot.exists()) {
docRef.get().addOnSuccessListener { documentSnapshot ->
val userInfo = documentSnapshot.toObject(UserInfo::class.java)
emailTV.text = userInfo?.email
}
} else {
Log.d(TAG, "Current data: null")
}
})
}
}
sendUserData() method code at Registration activity
private fun sendUserData() {
val name = userN.text.toString()
val email = userEm.text.toString()
val phone = userPhone.text.toString()
val addressName = addressName.text.toString()
val area = area.text.toString()
val block = block.text.toString()
val street = strees.text.toString()
val avenue = avenue.text.toString()
val house = house.text.toString()
val floor = floor.text.toString()
val apartment = apartment.text.toString()
val additionalInfo = additional.text.toString()
val address = "Addres Name: $addressName \n Area: $area \n B: $block ST: $street Av: $avenue H: $house\n " +
"Floor: $floor Apartment: $apartment \n Notes: $additionalInfo"
val userID = auth.currentUser?.uid
val userData = UserInfo(name, email, phone, address)
db.collection("users").document(userID!!).set(userData).addOnSuccessListener {
Toast.makeText(this, "Successfully Registered", Toast.LENGTH_SHORT).show()
}.addOnFailureListener{
Toast.makeText(this, "Data Upload error!", Toast.LENGTH_SHORT).show()
}
}
If you want to use a Kotlin data class with documentSnapshot.toObject, you're going to have to make each field a nullable var instead of val. The Firestore SDK doesn't know how to map document fields into data class constructor argument.
If you want a proper immutable data class with val fields, you're going to have to manually read each field out of the document, and call the data class constructor yourself.

Categories

Resources