Firebase #Exclude with kotlin data class - firebase

I have this data class in Kotlin (example):
import com.google.firebase.database.Exclude
data class User(val name: String = "", #Exclude val age: Int = 0)
And I don't want to save the age property in firebase. #Exclude should do this but it does not work, age is still saved.
Are there any workarounds?

Placing #Exclude on a property targets its generated field and not its generated get accesor method. To do the latter you'll need to prefix "Exclude" with "get:". e.g.:
data class User(val name: String = "", #get:Exclude val age: Int = 0)
See Annotation Use-site Targets for more details.

Actually you don't need to add only #get:Exclude but you need all 3 Exclude,
#Exclude #set:Exclude #get:Exclude.
I did it for imageUrl and providerId
data class FirebaseChatModel(
#get:PropertyName("message")
#set:PropertyName("message")
var message: String = "",
#get:PropertyName("type")
#set:PropertyName("type")
var type: Int = 1,
#get:PropertyName("senderId")
#set:PropertyName("senderId")
var senderId: Int = 0,
#get:PropertyName("receiverId")
#set:PropertyName("receiverId")
var receiverId: Int = 0,
#Exclude #set:Exclude #get:Exclude var imageUrl: String? = "",
#Exclude #set:Exclude #get:Exclude var providerId: Int = 0
)

Related

retrofit 2 post - getting internal server error (500)

I'm trying to use retrofit2 with Koltin in Android Studio as part of jetpack compose application. I'm sending a POST and keep getting error 500. I don't have access to the server code so I'm trying to figure out what am I doing wrong.
This is the interface I have declared for building the retrofit object:
I tried three different ways of declaring the POST endpoint.
#Singleton
interface IsrPayService {
#Headers ("Content-Type: application/json")
#POST("v3/driver/new-credit-driver")
suspend fun signUp(#Body user: UserDriver): Call\<WsError?\>
#Headers (
"Content-Type: application/json",
"Accept: application/json")
#POST("v3/driver/new-credit-driver")
suspend fun signup2(#Body user: UserDriver): retrofit2.Response<WsError>
#FormUrlEncoded
#POST("v3/driver/new-credit-driver")
suspend fun signupUrlEncoded(
#Field("firstName") firstName: String,
#Field("lastName") lastName: String): retrofit2.Response<WsError>
}
The data I am trying to send is UserDriver and I declared all the internal data classes below:
data class UserDriver(
#SerializedName("firstName") val firstName: String = "default",
#SerializedName("lastName") val lastName: String = "default",
#SerializedName("civilId") val civilId: String = "default",
#SerializedName("vehicleLicensingNumber") val vehicleLicensingNumber: String = "default",
#SerializedName("vehicleManufacturer") val vehicleManufacturer: String = "default",
#SerializedName("vehicleModel") val vehicleModel: String = "default",
#SerializedName("vehicleManufactureYear") val vehicleManufactureYear: String = "1973",
#SerializedName("counterModel") val counterModel: String = "default",
#SerializedName("authorizedEmployerNumber") val authorizedEmployerNumber: String = "default",
#SerializedName("bankAccountId") val bankAccountId: String = "default",
#SerializedName("bankAccountBranch") val bankAccountBranch: Int = 0,
#SerializedName("bankId") val bankId:Int = 123456,
#SerializedName("email") val email: String = "default",
#SerializedName("dob") val dob: DateIndicator = DateIndicator(date = 3, month = 4, year = 2023),
#SerializedName("address") val address: Address = Address (Coordinates(0,0),"מודיעין","טשרניחובסקי","12"),
#SerializedName("phoneNumber") val phoneNumber: String = "default",
#SerializedName("driverLicenseId") val driverLicenseId: Int = 0,
#SerializedName("civilIdPhoto") val civilIdPhoto: String = "default",
#SerializedName("driverLicensePhoto") val driverLicensePhoto: String = "default",
#SerializedName("signaturePhoto") val signaturePhoto: String = "default"
)
data class DateIndicator(
#SerializedName("date")
#Expose
val date: Int,
#SerializedName("month")
#Expose
val month: Int,
#SerializedName("year")
#Expose
val year: Int
)
I think #Expose is not required but tried it just in case..
data class Address (
#SerializedName("coordinates")
#Expose
val coordinates: Coordinates,
#SerializedName("city")
#Expose
val city: String,
#SerializedName("street")
#Expose
val street: String,
#SerializedName("number")
#Expose
val number: String
)
data class Coordinates (
#SerializedName("latitude")
#Expose
val latitude: Int,
#SerializedName("longitude")
#Expose
val longitude: Int
)
I tried to methods of getting the response:
suspend fun driverSignUp(user: UserDriver, onResult: (WsError?) -> Unit) {
try {
ws.signUp(user = user). enqueue (
object: Callback<WsError?> {
override fun onResponse(call: Call<WsError?>, response: Response<WsError?>) {
Log.d("driverSignUp",
"onResponse: response.isSuccessful = ${response.isSuccessful}")
var wsError: WsError? = null
wsError = if(!response.isSuccessful){
WsError(
body = "",
isError = true,
error = yz.learning.isrpaytest.model.Error(
errorCode = response.code(),
errorMessage = ErrorMessage(
enUs = response.message(),
heIl = response.message())))
} else {
response.body()
}
onResult(wsError)
}
override fun onFailure(call: Call<WsError?>, t: Throwable) {
Log.d("driverSignUp", "onFailure: ")
onResult(null)
}
}
)
} catch (exception: Exception) {
Log.d("driverSignUp", "driverSignUp exception: ${exception.message}")
onResult(
WsError(
body = "",
isError = true,
error = yz.learning.isrpaytest.model.Error(
errorCode = 0,
errorMessage = ErrorMessage(
enUs = exception.message!!,
heIl = exception.message!!)))
)
}
}
suspend fun driverSignUp2(user: UserDriver): retrofit2.Response<WsError>{
return ws.signup2(user)
}
I don't understand why I keep getting Internal server error. I have a feeling I have to send the data as a JSON string and not as an Object but as far as I understand this is supposed to be automatically using the gson converter, no?
I can try a simpler endpoint, but I think I will end up with the same problem.
I will appreciate any help since I'm stuck with this issue for a couple of days.
Thanks,
Yariv

Corda - VaultQuery with aggregate SUM (field Double in MappedSchema)

I need to summarize a column of a state. I created a mappedSchema and defined the field as Double.
If I list the states, the values for that field are correct. But if I use builder::sum(), the value returns with rounding problems and more decimal places than it should.
Here are excerpts from the code:
STATE
data class ConsumerMeteringState(val metering : ConsumerMetering,
val meteringParticipants : List<AbstractParty> = listOf(),
override val linearId: UniqueIdentifier = UniqueIdentifier()) :
LinearState, QueryableState {
override val participants: List<AbstractParty> = meteringParticipants
override fun generateMappedObject(schema: MappedSchema): PersistentState {
return when (schema) {
is ConsumerMeteringSchemaV1 -> ConsumerMeteringSchemaV1.PersistentConsumerMetering(
this.metering.dateTimeIni,
this.metering.dateTimeEnd,
this.metering.quantityKwh,
this.linearId.id
)
else -> throw IllegalArgumentException("Unrecognised schema $schema")
}
}
override fun supportedSchemas(): Iterable<MappedSchema> = listOf(ConsumerMeteringSchemaV1)
SCHEMA
object ConsumerMeteringSchemaV1 : MappedSchema(
schemaFamily = ConsumerMeteringSchema.javaClass,
version = 1,
mappedTypes = listOf(PersistentConsumerMetering::class.java)) {
#Entity
#Table(name = "consumer_metering_states")
class PersistentConsumerMetering(
#Column(name = "date_time_ini")
var dateTimeIni: Instant,
#Column(name = "date_time_end")
var dateTimeEnd: Instant,
#Column(name = "quantity_kwh")
var quantityKwh: Double,
#Column(name = "linear_id")
var linearId: UUID
) : PersistentState() {
// Default constructor required by hibernate.
constructor(): this(Instant.now(), Instant.now(), 0.0, UUID.randomUUID())
}
}
VAULTQUERY CRITERIA
val criteriaAccount = QueryCriteria.VaultQueryCriteria(externalIds = listOf(accountId))
val sumQuantityKwh = builder { ConsumerMeteringSchemaV1
.PersistentConsumerMetering::quantityKwh.sum() }
val sumQuantityKwhCriteria = QueryCriteria.VaultCustomQueryCriteria(sumQuantityKwh)
serviceHub.vaultService.queryBy(contractStateType = ConsumerMeteringState::class.java,
criteria = criteriaAccount.and(sumQuantityKwhCriteria)).otherResults.singleOrNull()
States only (the values are OK):
[ConsumerMeteringState(metering=ConsumerMetering(dateTimeIni=2020-06-03T09:46:00Z, dateTimeEnd=2020-06-03T09:59:00Z, quantityKwh=10.55), meteringParticipants=[Anonymous(DL624i3ieTdLkPRBUvUgZnzn5jeG3Md2cvANt6sZNJiXwy), O=Distributor, L=Curitiba, C=BR], linearId=2e5009ad-56c3-4fed-ba36-deb0d48e668c), ConsumerMeteringState(metering=ConsumerMetering(dateTimeIni=2020-06-03T09:46:00Z, dateTimeEnd=2020-06-03T09:59:00Z, quantityKwh=50.18), meteringParticipants=[Anonymous(DLBep6kdDduaMKVrszQWa7N8i6YNnJLtA4WXsp4QmZiEjC), O=Distributor, L=Curitiba, C=BR], linearId=3b012984-676d-4e62-9b9f-1bb8158aaf4b)]
With builder sum:
I get the value 60.730000000000004
Why sum doesn't return 60.73 ?
It worked by changing the column type from Double to BigDecimal. It seems to be some question of precision of the Double type.
I did a test just by retrieving the states and making a simple sum of the quantityKwh (Double) field and the precision was already strange.
I didn't understand the reason for this behavior, but with BigDecimal it worked ok.

VaultCustomQueryCriteria for State in Corda

I am trying to query a state from vault without using the linear Id of the state and instead an Int(unique) variable present in Schema
val sNumber = AState.ASchemaV1.AEntity::SNumber
val QueryCriteria = QueryCriteria.VaultCustomQueryCriteria(sNumber.equal(SalesNumber))
val StateAndRef = serviceHub.vaultService.queryBy<AState>(QueryCriteria).states.single()
val outState = StateAndRef.state.data
The Query criteria is not throwing any error but I am also not getting any output but on debugging I got an error response
javax.persistence.PersistenceException: org.hibernate.InstantiationException: No default constructor for entity: AState.ASchemaV1.AEntity
but I have defined all the columns in the function. What am I missing?
Here is code for Schema
override fun supportedSchemas() = listOf(ASchemaV1)
override fun generateMappedObject(schema: MappedSchema) = ASchemaV1.AEntity(this)
object ASchemaV1 : MappedSchema(AState::class.java, 1, listOf(AEntity::class.java)) {
#Entity
#Table(name = "Table")
class AEntity(A: AState) : PersistentState() {
#Column
var CONumber: String = A.linearId.id.toString()
#Column
var SalesNumber: Int = A.SalesNumber
#Column
var ProductID: Int = A.ProductID
#Column
var Quantity: Int = A.Quantity
#Column
var Rate: Double = A.Rate
#Column
var DeliveryDate: Date = A.DeliveryDate
#Column
var DeliveryLocation: String = A.DeliveryLocation
#Column
var Status: String = A.Status.toString()
}
}
AState.ASchemaV1 is missing the constructor.
object ASchemaV1 : MappedSchema(AState::class.java, 1, listOf(AEntity::class.java)) {
#Entity
#Table(name = "Table")
class AEntity(
#Column
var CONumber: String,
#Column
var SalesNumber: Int,
#Column
var ProductID: Int,
#Column
var Quantity: Int,
#Column
var Rate: Double,
#Column
var DeliveryDate: Date,
#Column
var DeliveryLocation: String,
#Column
var Status: String
): PersistentState() {
constructor(A: AState): this(A.linearId.id.toString(), A.SalesNumber, A.ProductID, A.Quantity, A.Rate, A.DeliveryDate, A.DeliveryLocation, A.Status.toString())
}
}

Firestore update array field in a Document

I have a document like below, in my document, have a Array . in array have Objects. How can i update new object to an Array.
As you see below i can add document in a colletion wit an array, but when i tried to update it gives error
java.lang.IllegalArgumentException: Invalid data. Unsupported type: com.test.data.modal.Product
What i tried;
var pid = ""
btnAdd.setOnClickListener {
val list = ArrayList<Product>()
list.add(Product("u1", "1", 1))
list.add(Product("u2", "2", 1))
list.add(Product("u3", "3", 1))
val testObject = TestObject("Talha", "Kosen", list)
FirebaseFirestore.getInstance().collection("Test")
.add(testObject)
.addOnCompleteListener { task ->
pid = task.result.id
}
}
btnUpdate.setOnClickListener {
val list = ArrayList<Product>()
list.add(Product("u1", "1", 1))
list.add(Product("u2", "2", 1))
list.add(Product("u3", "3", 1))
list.add(Product("u4", "4", 4))
FirebaseFirestore.getInstance()
.collection("Test")
.document(pid)
.update("product", list)
}
document:
POJO:
#IgnoreExtraProperties
#Keep
class TestObject {
var name: String = ""
var surname: String = ""
lateinit var productList: List<Product>
constructor()
constructor(name: String, surname: String, productList: List<Product>) {
this.name = name
this.surname = surname
this.productList = productList
}
}
#IgnoreExtraProperties
#Keep
class Product {
var imagePath: String = ""
var productUrl: String = ""
var brand: Int = 0
constructor()
constructor(imagePath: String, productUrl: String, brand: Int) {
this.imagePath = imagePath
this.productUrl = productUrl
this.brand = brand
}
}
I have probably not the answer but this could help :
You try to update with an ArrayList but you store a List (in your TestObject)
Refer to the documentation to update fields in nested objects https://firebase.google.com/docs/firestore/manage-data/add-data#update_fields_in_nested_objects
It could be a good idea to store a collection in your TestObject document instead of a list ? You'll able to update easily your fields

How to query on a field in nested collection of a parent state using VaultCustomQuery

I have a one-to-many relationship where I am trying to add a list of object/class in my state. i.e
I have a contract state that has a list of attachments List<Attachment>, where Attachmentis just a class with fields like
attachmentHash, uploadedDate, fileType
I wanted to query with something in the child but I get syntax error "AttachmentEntity is not a subtype of PersistentState"
QueryCriteria.VaultCustomQueryCriteria(
builder { (ContractSchemaV1.AttachmentEntity::uploadDate).equal(givenDate) }))
I let AttachmentEntity be a subclass of PersistentState and the node started up with the error
org.hibernate.AnnotationException: net.corda.core.schemas.PersistentStateRef
must not have #Id properties when used as an #EmbeddedId: project.schemas.ContractSchemaV1$AttachmentEntity.stateRef
Seems like I'm doing something wrong, whats the best way to represent a collection of data classes in the state and translate that in a schema? Or is this already the correct way, but there's no way to query the nested collection using VaultCustomQuery?
The example entity below.
object ContractSchema
object ContractSchemaV1 : MappedSchema(schemaFamily = ContractSchema.javaClass, version = 1,
mappedTypes = listOf(ContractEntity::class.java, AttachmentEntity:class.java)) {
#Entity
#Table(name = "contracts")
class ContractEntity(
#Column(name = "issued_date")
var issuedDate: Instant,
#Column(name = "linear_id")
var linearId: String,
#OneToMany(fetch = FetchType.LAZY, cascade = arrayOf(CascadeType.PERSIST))
#JoinColumns(
JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id"),
JoinColumn(name = "output_index", referencedColumnName = "output_index"))
var attachments: MutableSet<AttachmentEntity> = emptyList(),
) : PersistentState()
#Entity
#Table(name = "attachments")
class AttachmentEntity (
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false)
var id: Long? = null,
#Column(name = "attachment_hash", nullable = false)
var attachmentHash: String? = null,
#Column(name = "attachment_name", nullable = false)
var attachmentName: String? = null,
#Column(name = "upload_date", nullable = true)
var uploadDate: Instant? = null)
}
Your schema definition is correct (and you can see another example here: Querying nested collections in LinearState states).
However, querying nested collections is not supported by VaultCustomQueryCriteria. You have to do direct JDBC queries to query attributes of the nested collections.
Here is an example of a direct JDBC query in Corda:
#Test
fun `test calling an arbitrary JDBC native query`() {
val nativeQuery = "SELECT v.transaction_id, v.output_index FROM vault_states v WHERE v.state_status = 0"
database.transaction {
val jdbcSession = services.jdbcSession()
val prepStatement = jdbcSession.prepareStatement(nativeQuery)
val rs = prepStatement.executeQuery()
var count = 0
while (rs.next()) {
val stateRef = StateRef(SecureHash.parse(rs.getString(1)), rs.getInt(2))
Assert.assertTrue(cashStates.map { it.ref }.contains(stateRef))
count++
}
Assert.assertEquals(cashStates.count(), count)
}
}

Resources