CRUD with table relationship using KTOR and EXPOSED - ktor

I'm having problems with KTOR and EXPOSED for a crud using relationship between tables.
I configured my service as follows:
class LegalPersonService {
suspend fun findAll(): List<LegalPerson> = dbQuery {
LegalPersons.selectAll().map { toLp(it) }
}
suspend fun insert(lp: LegalPerson, ph: Phone) = dbQuery {
LegalPersons.insert {
it[id] = lp.id
it[internalId] = lp.internalId
it[companyId] = lp.companyId
it[active] = lp.active
it[tradeName] = lp.tradeName
it[fantasyName] = lp.fantasyName
it[email] = lp.fantasyName
it[cnpj] = lp.cnpj
it[stateRegistration] = lp.stateRegistration
it[muninipalRegistration] = lp.muninipalRegistration
it[address] = lp.address
}.let {
Phones.insert {
it[id] = ph.id
it[internalId] = ph.internalId
it[phone] = ph.phone
}
}
}
private fun toLp(row: ResultRow): LegalPerson =
Phone(
id = row[Phones.id],
internalId = row[Phones.internalId],
phone = row[Phones.phone]
).let {
LegalPerson(
id = row[LegalPersons.id],
internalId = row[LegalPersons.internalId],
companyId = row[LegalPersons.companyId],
active = row[LegalPersons.active],
tradeName = row[LegalPersons.tradeName],
fantasyName = row[LegalPersons.fantasyName],
email = row[LegalPersons.email],
cnpj = row[LegalPersons.cnpj],
stateRegistration = row[LegalPersons.stateRegistration],
muninipalRegistration = row[LegalPersons.muninipalRegistration],
address = row[LegalPersons.address]
)
}
}
And my model:
// *** LEGAL PERSONS ***
data class LegalPerson(
val id: UUID,
val internalId: Long,
val companyId: UUID,
val active: Boolean,
val tradeName: String,
val fantasyName: String,
val email: String,
val cnpj: String,
val stateRegistration: String,
val muninipalRegistration: String,
val address: UUID
)
object LegalPersons : Table("person.legal_person") {
val id: Column<UUID> = uuid("id").autoIncrement().primaryKey()
val internalId: Column<Long> = long("internal_id").autoIncrement()
val companyId: Column<UUID> = uuid("company_id")
val active: Column<Boolean> = bool("active")
val tradeName: Column<String> = varchar("trade_name", 100)
val fantasyName: Column<String> = varchar("fantasy_name", 100)
val email: Column<String> = varchar("email", 100)
val cnpj: Column<String> = varchar("cnpj", 18)
val stateRegistration: Column<String> = varchar("state_registration", 20)
val muninipalRegistration: Column<String> = varchar("municipal_registration", 20)
val address: Column<UUID> = uuid("address")
}
// *** PHONES ***
data class Phone(
val id: UUID,
val internalId: Long,
val phone: UUID
)
object Phones : Table("person.phone_legal_person") {
val id: Column<UUID> = reference("id", LegalPersons.id).primaryKey()
val internalId: Column<Long> = long("internal_id").autoIncrement()
val phone: Column<UUID> = uuid("phone")
}
But I'm getting this error when I'm trying to insert data:
ERROR Application - Unhandled: POST - /api/clients/lp/
io.ktor.gson.UnsupportedNullValuesException: Receiving null values is not supported
Could anyone help? I am using DLS as opposed to DAO.
I am having difficulty, since the documentation is still being made.

After reading a little I found out what was wrong.
My service looks like this:
suspend fun insert(lp: LegalPerson) = dbQuery {
val ids = UUID.randomUUID()
LegalPersons.insert {
it[id] = ids
it[companyId] = lp.companyId
it[active] = lp.active
it[tradeName] = lp.tradeName
it[fantasyName] = lp.fantasyName
it[email] = lp.fantasyName
it[cnpj] = lp.cnpj
it[stateRegistration] = lp.stateRegistration
it[muninipalRegistration] = lp.muninipalRegistration
it[address] = lp.address
}
Phones.batchInsert(lp.phones) { phone ->
this[Phones.id] = ids
this[Phones.phone] = phone.phone
}
}

Related

How to test a service function in kotlin which uses a late init Id?

I am new to testing in kotlin and I was wondering how I can test this function:
this is my SegmentService file:
fun createSegmentFromUserIds(userIds: List<String>, name:String, appId: String): Segmentation {
val filter = createUserIdFilter(userIds)
val createSegmentRequest = CreateSegmentRequest(
name = name, attribute = filter, type = SegmentType.STATIC
)
val segmentation = segmentMetaService.saveSegmentInfo(createSegmentRequest, appId)
querySegmentService.runUpdateQuery(users = userIds, appId = appId, segmentId = segmentation.id)
return segmentation
}
this is the saveSegmentInfo function:
fun saveSegmentInfo(createSegmentFilter: CreateSegmentRequest, appId: String): Segmentation {
val segmentInfo = segmentationRepository.save(createSegmentFilter.let {
Segmentation(
attribute = it.attribute, name = it.name, appId = appId, type = it.type
)
})
logger.info("Segment info saved with id: ${segmentInfo.id}, name: ${segmentInfo.name}")
return segmentInfo
}
and this is the Segmentation Document
#Document(Segmentation.COLLECTION_NAME)
class Segmentation(
#Field(ATTRIBUTE)
val attribute: Filter,
#Field(NAME)
val name: String,
#Indexed
#Field(APP_ID)
val appId: String,
#Field(STATUS)
var status: SegmentStatus = SegmentStatus.CREATED,
#Field(TYPE)
var type: SegmentType = SegmentType.STATIC,
#Field(USER_COUNT)
var userCount: Long? = null,
) {
#Id
lateinit var id: String
#Field(CREATION_DATE)
var creationDate: Date = Date()
}
I have written this test for it:
class SegmentServiceTest {
private val segmentMetaService = mock(SegmentMetaService::class.java)
private val querySegmentService = mock(QuerySegmentService::class.java)
private val converterService = mock(ConverterService::class.java)
private val userAttributeService = mock(UserAttributeService::class.java)
private val segmentConsumerUserInfoProducer = mock(SegmentConsumerUsersInfoProducer::class.java)
private val segmentationRepository = mock(SegmentationRepository::class.java)
#Test
fun `createSegmentFromUserIds should create a new segment`() {
val segmentService = SegmentService(
segmentMetaService = segmentMetaService,
querySegmentService = querySegmentService,
converterService = converterService,
userAttributeService = userAttributeService,
segmentConsumerUserInfoProducer = segmentConsumerUserInfoProducer
)
val userIds = listOf("user-1", "user-2", "user-3")
val filter = AndFilter(
operations = listOf(
AndFilter(
operations = listOf(
StringListContainsFilter(
field = "userId", operationType = StringQueryOperationType.IN, values = userIds
)
)
)
)
)
val createSegmentRequest = CreateSegmentRequest(
name = "segment-name", attribute = filter, type = SegmentType.STATIC
)
val segment = Segmentation(attribute = filter, name = "segment-name", type = SegmentType.STATIC, appId = "app-id" )
`when`(segmentationRepository.save(any())).thenReturn(segment)
`when`(segmentMetaService.saveSegmentInfo(createSegmentRequest, "app-id")).thenReturn(segment)
val createdSegment = segmentService.createSegmentFromUserIds(userIds = userIds, name = "segment-name", appId = "app-id")
assertEquals(segment, createdSegment)
}
}
but I am facing this error:
kotlin.UninitializedPropertyAccessException: lateinit property id has not been initialized
So what am I doing wrong here?
How can I initialize the Id? or should I refactor my code so for it to become testable?
I think you are mocking / providing the answers to the wrong calls.
you mock segmentationRepository.save(..) but this call won't ever be made since that call is inside segmentMetaService.saveSegmentInfo(...) which is mocked so the real code is not called.
when you call segmentService.createSegmentFromUserIds(..), my guess (stack trace would be useful on the error, BTW), is that this method proceeds passed the invocation of segmentMetaService.saveSegmentInfo(...) but in the next line you call querySegmentService.runUpdateQuery(users = userIds, appId = appId, segmentId = segmentation.id) which is accessing the uninitialised id.
The fix will be to set the segment.id when you set up the object for the answer to segmentMetaService.saveSegmentInfo(..)

Kafka Exception: The group member needs to have a valid member id before actually entering a consumer group

I have this consumer that appears to be connected to the kafka topic but after seeing the logs I'm seeing that the consumer Join group failed with org.apache.kafka.common.errors.MemberIdRequiredException: The group member needs to have a valid member id before actually entering a consumer group. I've tried looking at a few other setups online and I don't see anyone explicitly setting a member ID. I don't see this exception thrown locally so I'm hoping that maybe I just need to do some fine-tuning on the consumer configurations but I'm not sure what needs to be changed.
Kafka Consumer
#KafkaListener(
topics = ["my.topic"],
groupId ="kafka-consumer-group"
)
fun consume(consumerRecord: ConsumerRecord<String, Record>, ack: Acknowledgment) {
val value = consumerRecord.value()
val eventKey = consumerRecord.key()
val productNotificationEvent = buildProductNotificationEvent(value)
val consumerIdsByEventKey = eventKey.let { favoritesRepository.getConsumerIdsByEntityId(it) }
ack.acknowledge()
}
Kafka Consumer Config
#EnableKafka
#Configuration
class KafkaConsumerConfig(
#Value("\${KAFKA_SERVER}")
private val kafkaServer: String,
#Value("\${KAFKA_SASL_USERNAME}")
private val userName: String,
#Value("\${KAFKA_SASL_PASSWORD}")
private val password: String,
#Value("\${KAFKA_SCHEMA_REGISTRY_URL}")
private val schemaRegistryUrl: String,
#Value("\${KAFKA_SCHEMA_REGISTRY_USER_INFO}")
private val schemaRegistryUserInfo: String,
#Value("\${KAFKA_CONSUMER_MAX_POLL_IN_MS}")
private val maxPollIntervalMsConfig: Int,
#Value("\${KAFKA_CONSUMER_MAX_POLL_RECORDS}")
private val maxPollRecords: Int
) {
#Bean
fun consumerFactory(): ConsumerFactory<String?, Any?> {
val props: MutableMap<String, Any> = mutableMapOf()
props[ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG] = kafkaServer
props[ConsumerConfig.GROUP_ID_CONFIG] = KAFKA_CONSUMER_GROUP_ID
props[ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG] = StringDeserializer::class.java
props[ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG] = KafkaAvroDeserializer::class.java
props[ConsumerConfig.AUTO_OFFSET_RESET_CONFIG] = "earliest"
props[ConsumerConfig.MAX_POLL_RECORDS_CONFIG] = maxPollRecords
props[ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG] = maxPollIntervalMsConfig
props[CommonClientConfigs.SECURITY_PROTOCOL_CONFIG] = "SASL_SSL"
props[SaslConfigs.SASL_MECHANISM] = "PLAIN"
val module = "org.apache.kafka.common.security.plain.PlainLoginModule"
val jaasConfig = String.format(
"%s required username=\"%s\" password=\"%s\";",
module,
userName,
password
)
props[SaslConfigs.SASL_JAAS_CONFIG] = jaasConfig
props[KafkaAvroDeserializerConfig.VALUE_SUBJECT_NAME_STRATEGY] = TopicRecordNameStrategy::class.java
props[KafkaAvroDeserializerConfig.SCHEMA_REGISTRY_URL_CONFIG] = schemaRegistryUrl
props[KafkaAvroDeserializerConfig.BASIC_AUTH_CREDENTIALS_SOURCE] = "USER_INFO"
props[KafkaAvroDeserializerConfig.USER_INFO_CONFIG] = schemaRegistryUserInfo
return DefaultKafkaConsumerFactory(props)
}
#Bean
fun kafkaListenerContainerFactory(): ConcurrentKafkaListenerContainerFactory<String, Any>? {
val factory = ConcurrentKafkaListenerContainerFactory<String, Any>()
factory.consumerFactory = consumerFactory()
factory.containerProperties.ackMode = ContainerProperties.AckMode.MANUAL_IMMEDIATE
factory.containerProperties.isSyncCommits = true
return factory
}
companion object {
private const val KAFKA_CONSUMER_GROUP_ID = "kafka-consumer-group"
}
}

How to join three lists into a single Object

How to transform three lists based on userId in a single object, where each object has the respected list object as a variable.
data class User(val id: String)
data class Book(val id: String, val userId: String)
data class Order(val id: String, val userId: String)
Input
val users: List<User> = listOf(
User(id = "u1"),
User(id = "u2"),
User(id = "u3")
)
val books: List<Book> = listOf(
Book(id = "b1.u1", userId = "u1"),
Book(id = "b2.u1", userId = "u1"),
Book(id = "b3.u2", userId = "u2"),
Book(id = "b4.ux", userId = "ux")
)
val order: List<Order> = listOf(
Order(id = "o1", userId = "u1"),
Order(id = "o2", userId = "u1"),
Order(id = "03", userId = "u2"),
Order(id = "o4", userId = "u1")
)
Output
val result = listOf(Result(user, book, order))
You can group the books and orders by userId and then for each user you can pick the corresponding books and orders.
val booksMap = books.groupBy { it.userId }
val ordersMap = orders.groupBy { it.userId }
users.map {
Result(
user = it,
books = booksMap[it.id] ?: emptyList(),
orders = ordersMap[it.id] ?: emptyList()
)
}
Here Result is:
data class Result(val user: User, val books: List<Book>, val orders: List<Order>)
Hard to get exactly what you need as output. To me, it makes the most sense to get a list of books and orders for each user. If that is the case, then you can do this:
data class Result(val user: User, val books: List<Book>, val orders: List<Order>)
val results = users.map { user ->
val userBooks = books.filter { it.userId == user.id }
val userOrders = orders.filter { it.userId == user.id }
Result(user, userBooks, userOrders)
}

FOREIGN KEY constraint failed (code 787 SQLITE_CONSTRAINT_FOREIGNKEY) when add data to table room

I have to tables CheckListModel and CheckListPoints, it is one to n relationship, when i try to add
data in DB CheckListModel adds correctly, but when code gose to add CheckListPoints i got this error. I have no idea why this happening
This is my DB
#Database(entities = [CheckListModel::class,CheckListPoints::class],version = 4,exportSchema = false)
abstract class CheckListDB : RoomDatabase() {
abstract fun checkListDBDao():CheckListModelDBDao
companion object {
#Volatile
private var instance: CheckListDB? = null
fun getInstance(context: Context):CheckListDB{
return instance ?: synchronized(this){
instance?: buildDatabase(context).also { instance = it }
}
}
private fun buildDatabase(context: Context): CheckListDB{
return Room.databaseBuilder(context,CheckListDB::class.java,"check_list_model").fallbackToDestructiveMigration().build()
}
}
}
entityes
#Entity(
tableName = "check_list_point",
foreignKeys = [
ForeignKey(entity = CheckListModel::class, parentColumns = ["checkListModelID"],childColumns = ["checkListColumnID"],onDelete = ForeignKey.CASCADE)
],
indices = [Index("checkListColumnID")]
)
data class CheckListPoints(
#ColumnInfo(name = "correctly")
var correctly: Boolean,
#ColumnInfo(name = "requirement")
var requirement: String,
#ColumnInfo(name = "passed")
var passed: Boolean,
#ColumnInfo(name="checkListColumnID")
val checkListColumnID: Long,
#PrimaryKey(autoGenerate = true)
val checkListPointsModelID: Long = 0L
): Serializable
#Entity(tableName = "check_list_model")
data class CheckListModel (
#ColumnInfo(name = "check_list_name")
val checkListName: String,
#ColumnInfo(name = "check_list_count")
val checkListCount: Int,
#ColumnInfo(name = "check_list_result")
val checkListResult: Int,
#ColumnInfo(name = "description")
val description: String,
#PrimaryKey(autoGenerate = true)
val checkListModelID: Long = 0L
) : Serializable
relationship
data class CheckListWithCheckListModel(
#Embedded val CheckList: CheckListModel,
#Relation(
parentColumn = "checkListModelID",
entityColumn = "checkListColumnID"
)
val checkListPoints: List<CheckListPoints>
)
this is dao
#Dao
interface CheckListModelDBDao {
#Insert
fun insertCheckList(data:CheckListModel)
#Insert
fun insertCheckListPoint(vararg data:CheckListPoints)
#Delete
fun deleteCheckList(checkList: CheckListModel)
#Transaction
#Query("SELECT * FROM check_list_model " )
fun getEverything(): Flow<List<CheckListWithCheckListModel>>
}
and this is how i add
private var doorCheckListModel = CheckListModel("Дверь",0,0,"4321")
private val doorCheckListPoint1 = CheckListPoints(false,"1",false,doorCheckListModel.checkListModelID)
private val doorCheckListPoint2 = CheckListPoints(false,"2",false,doorCheckListModel.checkListModelID)
private var doorListOfCheckListPoints = listOf<CheckListPoints>(doorCheckListPoint1,doorCheckListPoint2)
private var windowCheckListModel = CheckListModel("Окно",0,0,"4321")
private var windowCheckListPoint1 = CheckListPoints(false,"1",false,windowCheckListModel.checkListModelID)
private var windowCheckListPoint2 = CheckListPoints(false,"1",false,windowCheckListModel.checkListModelID)
private var windowListOfCheckListPoints = listOf<CheckListPoints>(windowCheckListPoint1,windowCheckListPoint2)
var checkLists = MutableLiveData<List<CheckListModel>>().apply {
value = listOf(doorCheckListModel,windowCheckListModel)
}
fun addCheckList(name: String){
viewModelScope.launch(Dispatchers.IO) {
when (name) {
"Дверь" -> insert(doorCheckListModel,doorListOfCheckListPoints)
"Окно" -> insert(windowCheckListModel,windowListOfCheckListPoints)
}
}
}
private suspend fun insert(checkList: CheckListModel, checkListPoints: List<CheckListPoints>){
database.insertCheckList(checkList)
for(checkListPoint in checkListPoints){
database.insertCheckListPoint(checkListPoint)
}
}
}
also i display data from CheckListModel in fragment. CheckListModel added to DB Correctly and display correctly, but CheckListPoints has not
When you create doorCheckListModel, its checkListModelID is initially 0. You use this 0 as checkListColumnID in doorCheckListPoint1. So when you save the CheckListModel, Room automatically generates the primary key and saves in the table. Similar is the case for primary key in CheckListPoints table. But the entries saved in CheckListPoints table still have 0 in checkListColumnID column.
This is why the foreign key constraint is failing. There is no CheckListModel with 0 as its primary key. To fix this, you will have to set the value of checkListColumnID before saving a CheckListPoints entry in the table.
If you go through Room documentation, the #Insert annotated function can optionally return the rowId for the inserted item. For integer primary keys, rowId is the same as primary key.
Try this code:
// Return the primary key here
#Insert
fun insertCheckList(data:CheckListModel): Long
private suspend fun insert(checkList: CheckListModel, checkListPoints: List<CheckListPoints>){
val id = database.insertCheckList(checkList)
for(checkListPoint in checkListPoints){
database.insertCheckListPoint(checkListPoint.copy(checkListColumnID = id))
}
}

Kotlin Exposed ORM - id not in record set

New to Kotlin Exposed and trying to store data using IdTable and getting errror 'id is not in record set.' Below is the code using
object GeoLocationModel : IdTable<Long>("geolocation_pos") {
val code = long("code").primaryKey()
val createdAt = datetime("created_at").clientDefault { currentUtc() }
val updatedAt = datetime("updated_at").nullable()
val createdBy = varchar("created_by", 255).clientDefault { authenticatedUser() }
val updatedBy = varchar("updated_by", 255).nullable()
val uuid = uuid("uuid").autoGenerate().uniqueIndex()
val latitude: Column<Double> = double("latitude")
val longitude: Column<Double> = double("longitude")
val pincode: Column<Long> = long("pincode")
override val id: Column<EntityID<Long>> = code.entityId()
}
object GeoLocationModel : IdTable<Long>("geolocation_pos") {
val code = long("code").primaryKey()
val createdAt = datetime("created_at").clientDefault { currentUtc() }
val updatedAt = datetime("updated_at").nullable()
val createdBy = varchar("created_by", 255).clientDefault { authenticatedUser() }
val updatedBy = varchar("updated_by", 255).nullable()
val uuid = uuid("uuid").autoGenerate().uniqueIndex()
val latitude: Column<Double> = double("latitude")
val longitude: Column<Double> = double("longitude")
val pincode: Column<Long> = long("pincode")
override val id: Column<EntityID<Long>> = code.entityId()
}
Insertion of data for **geolocation_pos** table
val geolocationId = GeoLocationModel.insert {
it[code] = nextSequenceVal()
it[latitude] = 28.550667
it[longitude] = 77.268952
it[pincode] = xxxxxx
} get GeoLocationModel.id
MySQL database is not able to save throws an error stating
java.lang.IllegalStateException: com.xxxx.xxxx.xxxx.xxx.GeoLocationModel.code is not in record set
at org.jetbrains.exposed.sql.ResultRow.getRaw(ResultRow.kt:53) ~[exposed-core-0.21.1.jar:na]
at org.jetbrains.exposed.sql.ResultRow.get(ResultRow.kt:18) ~[exposed-core-0.21.1.jar:na]
at org.jetbrains.exposed.sql.statements.InsertStatement.get(InsertStatement.kt:20) ~[exposed-core-0.21.1.jar:na]
Generated SQL
INSERT INTO geolocation_pos (code, created_at, created_by, latitude, longitude, pincode, updated_at, updated_by, uuid) VALUES (106, 'xxxxxxx', 'xxx', 28.550667, 77.268952, xxxxx, NULL, NULL, 'dd6aaf69-5324-4fac-9271-5c099957e06b')
Please can anyone help me resolving the issue.