Kotlin Data Class: How generate an MongoDB ObjectId for an embedded document - mongodb

I'm building an API with Spring Boot and Kotlin. I am trying to generate a structure the following way in MongoDB.
I understand that in MongoDb the concept of relationships between entities does not exist, so I will use the strategy of embedded documents. That is, to embed the Reunión in the Proyecto, and the Participante in the Reunión.
I have a main class called Proyecto and NewProyecto, that contains as property a list of reuniones of type NewReunion. I use two different classes to create and return data.
Proyecto.kt
#Document(collection = "proyectos")
#TypeAlias("proyecto")
data class Proyecto (
#Id
val id: String,
val nombre: String,
val area: String,
val fecha:String,
val reuniones: List<Reunion>?
){}
#Document(collection = "proyectos")
#TypeAlias("newproyecto")
data class NewProyecto (
#Id
val id: String?,//Es posiblemente nulo porqué se crea automáticamente
var nombre: String,
var area: String,
var fecha:String,
var reuniones: List<NewReunion>?
){}
Now, to create 'reuniones' I have two classes, Reunion and NewReunion. The class that corresponds to create a MongoDB embedded document is NewReunion.
NewReunion.kt
#Document
data class Reunion(
val objetivo: String,
val fecha: String,
val participantes: List<Participante>?
) {}
#Document
data class NewReunion(
var id: String? = ObjectId().toHexString(),
var fecha: String,
var participantes: List<NewParticipante>?
) {}
This is where I have the problem. I want to generate an ObjectId for this NewReunion class, so that each object embedded in it has an id. The problem is that ObjectId ().ToHexString() is not generating any value at the time that the object of type NewReunion is built, but the other data that are objetivo and fecha are filled with the data that comes from the request POST.
How I send the information.
The information I send via POST. This request is handled by a Controller named ProyectoController.kt
ProyectoController.kt
#PostMapping("/")
fun createProyecto(#RequestBody newProyecto: NewProyecto): NewProyecto = proyectoService.createProyecto(newProyecto)
ProyectoRepository.kt
interface ProyectoRepository : MongoRepository<Proyecto, String> {
fun findById(id: ObjectId): Proyecto
override fun findAll(): List<Proyecto>
fun insert(proyecto: NewProyecto): NewProyecto
fun save(proyect: Proyecto): Proyecto
fun deleteById(id: ObjectId)
}
ProyectoService.kt
#Service("proyectoService")
class ProyectoServiceImpl : ProyectoService {
#Autowired
lateinit var proyectoRepository: ProyectoRepository
//Obtener un proyecto
override fun findById(id: ObjectId): Proyecto = proyectoRepository.findById(id)
//Obtener todos los proyectos
override fun findAll(): List<Proyecto> = proyectoRepository.findAll()
//Crear un proyecto
override fun createProyecto(newProyecto: NewProyecto): NewProyecto = proyectoRepository.insert(newProyecto)
//Actualizar un proyecto
override fun updateProyecto(proyecto: Proyecto):Proyecto = proyectoRepository.save(proyecto)
//Eliminar un proyecto
override fun deleteProyecto(id:ObjectId) = proyectoRepository.deleteById(id)
}
POST using Postman:
To send the information I am using Postman, and I send the request in the following way.
At the time of creating the new Proyecto, I return it to see the result which returns a result with id=null, but all other fields do assign the corresponding value:
Now, I tried initializing all the constructor parameters of the NewReunion class to see what happened.
data class NewReunion(
#Id
var id: String? = ObjectId().toHexString(),
var objetivo: String = "",
var fecha: String = ""
) {}
the value for the id is generated correctly together with the other values. It is just this behavior that I do not understand why I need to initialize constructor parameters of the NewReunion class.
Result of POST with the parameters initialized.
build.gradle
buildscript {
ext.kotlin_version = '1.2.71'
ext {
kotlinVersion = '1.2.71'
springBootVersion = '2.0.6.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
classpath("org.jetbrains.kotlin:kotlin-allopen:${kotlinVersion}")
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: 'kotlin'
apply plugin: 'kotlin-spring'
apply plugin: 'eclipse-wtp'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'war'
group = 'com.gibranlara'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
compileKotlin {
kotlinOptions {
freeCompilerArgs = ["-Xjsr305=strict"]
jvmTarget = "1.8"
}
}
compileTestKotlin {
kotlinOptions {
freeCompilerArgs = ["-Xjsr305=strict"]
jvmTarget = "1.8"
}
}
repositories {
mavenCentral()
}
configurations {
providedRuntime
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" // Required for Kotlin integration
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
compile "org.springframework.boot:spring-boot-starter-data-mongodb"
compile 'org.springframework.boot:spring-boot-starter-web'
}

The library you are using is probably not written with Kotlin in mind.
Kotlin generates a synthetic constructor that loads default values prior to calling the actual constructor, e.g.
// Java
public NewReunion(String var1, String var2, String var3, int var4, DefaultConstructorMarker var5) {
if ((var4 & 1) != 0) {
var1 = ObjectId().toHexString();
}
this(var1, var2, var3);
}
The library is likely doing one of the following:
Calling the default constructor, then calling set[Property] matching the annotations/convention.
Calling the closest match constructor: NewReunion(#Nullable String id, #NotNull String objetivo, #NotNull String fecha) with NewReunion(null, "objetivo", "fecha")
If you define your class as such:
data class NewReunion #JvmOverloads constructor(
var id: String? = "",
var objetivo: String,
var fecha: String
)
You will get additional constructors e.g.
// Java
public NewReunion(#NotNull String objetivo, #NotNull String fecha)
If your library is using the first option then you may need to lazy initialize the id field in a getter (also convert data class to normal class).
An Aside
Most of these kind of problems stem from devs using the same object model for communication and business logic. Whenever I see a nullable id on an entity it like a clarion call that bugs are afoot.
Any data you get from an outside source(even if it's from a server you control) should be treated as if it was put there by your most baleful enemy, but many developers just suck it in and use it as it comes.
If you don't have something along the lines of
val cleanData = validate(inputData)
before crossing from an input layer to a business layer then you are setting yourself up for future embarrassment.
Input layers are:
User interface
Web services
Anything coming from outside your immediate domain of control

Related

ReactiveStreamCrudRepository not returning data from postgres DB

I'm new to reactive programming and micronaut. I'm basically working on simple CRUD APIs. I'm using Kotlin with micronaut. I'm not sure why the DB is not returning any Data and I'm stuck with this.
#JdbcRepository(dialect = Dialect.POSTGRES)
interface EmployeeCrudRepository: ReactiveStreamsCrudRepository<EmployeeMaster, Int>, EmployeeRepository {
}
interface EmployeeRepository {
fun findByEmployeeIdAndTcin(employeeId: UUID, tcin: String): Mono<EmployeeMaster>
}
#MappedEntity
#Table(name="employee")
data class EmployeeMaster (
#Id
#Column(name = "transaction_id")
val transactionId: Int,
#Column(name = "employee_id")
val employeeId: UUID,
#Column(name = "item_id")
val itemId: UUID
)
fun getEmployeeDetailsResponse(registryId: UUID, itemId: String) : Mono<EmployeeDetailsDTO> {
return getEmployeeDetails(employeeId, itemId)
.map {
employeeDetails -> EmployeeDetailsDTO(employeeDetails)
}
.switchIfEmpty {
logger.info("No records found")
Mono.just(ItemDetailsDTO())
}
}
fun getEmployeeDetails(employeeId: UUID, itemId: String) : Mono<EmployeeDetailsDTO> {
return employeeRepository.findByEmployeeIdAndTcin(registryId = registryId, tcin = itemId)
.map {
employeeDetails -> EmployeeDetailsDTO(employeeDetails)
}
.switchIfEmpty {
logger.info("No records found")
Mono.just(EmployeeDetailsDTO())
}
}
I'm confused as to how to debug this to find the issue. The credentials all seem to be fine and the record I'm searching for exists in the DB.
flyway {
// ./gradlew -Ppostgres_host=localhost -Ppostgres_ssl='' -Ppostgres_user=postgres -Ppostgres_pwd=postgres flywayMigrate -i
url = "jdbc:postgresql://${postgres_host}:5432/postgres${postgres_ssl}"
user = "${postgres_user}"
password = "${postgres_pwd}"
schemas = ['public']
}
Issue Found:
My Bad, I was sending some other value and didn't realise that the value was incorrect. The implementation was fine and returning the response as expected. I'm writing kotlin and micronaut code for the first time and at the back of my head it always feels like the implementation was wrong.
Take a look at Access a Database with Micronaut Data R2DBC.
There are a number of things in your example that look wrong:
#JdbcRepository(dialect = Dialect.POSTGRES) should be #R2dbcRepository.
#Table and #Column are JPA.
#MappedEntity("employee")
#MappedProperty("item_id")
You might not need #MappedProperty("item_id"), itemId should be mapped to item_id.

Generating auto-incrementing Instance IDs in Swift using Protocols and protocol-extensions only

Goal
To create an "AutoIDable" protocol with the following behaviour.
Every instance of a class conforming to this protocol will get an auto-generated "id" property of String type.
The code should generate id strings in the format <prefix><Instance-count-starting-from-1> (Eg: E-1, E-2, ...E-<n> and so on for 1st , 2nd ... nth Instance of the conforming class.
The protocol & protocol extensions should do ALL of the required work to generate the id strings. The conforming class will only have to subscribe to the protocol and nothing more.
Current status:
I have achieved Goal-1 & Goal-2 with the following implementation:
protocol Identifiable {
var id: String { get }
}
protocol AutoIDable: Identifiable{
static var _instanceCount: Int { get set }
}
class AutoID: AutoIDable {
init(idPrefix: String) {
setAutoID(prefix: idPrefix)
}
internal static var _instanceCount: Int = 0
var id: String = ""
func setAutoID(prefix: String = ""){
Self._instanceCount += 1
self.id = "\(prefix)\(Self._instanceCount)"
}
}
class Employee: AutoID {
init(){
super.init(idPrefix: "E-")
}
}
let e1 = Employee()
let e2 = Employee()
let e3 = Employee()
print(e1.id)
print(e2.id)
print(e3.id)
print(e1.id)
The output from running the above code:
E-1
E-2
E-3
E-1
Todo:
To achieve Goal-3, I need to eliminate the AutoID superclass and implement the same functionality using protocol extensions.
I ran into trouble because:
Protocol extensions do not allow static stored properties. I do know how to work around this limitation without using a superclass.
I do not know how to inject code into all the initialisers the creator of the Employee class might create. Again, I could not think of a workaround without using a superclass.
I would be grateful if you can point me in the right direction.
PS: New to Swift programming. If you’ve suggestions for implementing the code in a more “swifty” way, please do let me know. :-)
Since you want to use protocols, you can't have a stored property in the protocol. So, you'll need some place to store the incrementing ID value, if not the IDs themselves.
Not sure if it violates your requirements of using only protocols, because it would require a type for storage, but at least it won't require conforming classes to have a superclass.
So, let's say we build such a class that holds all the IDs and keeps the incrementing counter:
class AutoIncrementId {
static private var inc: Int = 0
static private var ids: [ObjectIdentifier: String] = [:]
static func getId(_ objectId: ObjectIdentifier, prefix: String) -> String {
if let id = ids[objectId] { return id }
else {
inc += 1
let id = "\(prefix)\(inc)"
ids[objectId] = id
return id
}
}
}
Then the protocol requirement could be:
protocol AutoIdentifiable {
static var prefix: String { get }
var id: String { get }
}
So, a class would need to define its prefix. But we could define a default implementation for id:
extension AutoIdentifiable where Self: AnyObject {
var id: String {
AutoIncrementId.getId(ObjectIdentifier(self), prefix: Self.prefix)
}
}
The usage would be:
class Employee: AutoIdentifiable {
static let prefix = "E-"
}
let e1 = Employee()
let e2 = Employee()
let e3 = Employee()
print(e1.id) // E-1
print(e2.id) // E-2
print(e3.id) // E-3
print(e1.id) // E-1

Kotlin use SpringData Jpa custom repository

This is my code. I customized my repository.
interface ChapterDao {
fun test(novelId:String):List<Chapter>
}
class ChapterDaoImpl constructor(#PersistenceContext var entityManager: EntityManager){
fun test(novelId: String): List<Chapter> {
val query = entityManager.createNativeQuery("select c.name, c.number from chapter c where c.novel.id = $novelId")
val resultList = query.resultList as Array<Array<Any>>
var chapterList:ArrayList<Chapter> = ArrayList<Chapter>()
for (item in resultList){
chapterList.add(Chapter(item.get(0) as String,item.get(1) as Int))
}
return chapterList
}
}
interface ChapterRepository : CrudRepository<Chapter, String>, ChapterDao {
}
Chapter code is:
package com.em248.entity;
import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonInclude
import java.util.*
import javax.persistence.*
#Entity
#Table(name = "chapter")
#com.fasterxml.jackson.annotation.JsonInclude(JsonInclude.Include.NON_EMPTY)
class Chapter {
#Id
var id: String = UUID.randomUUID().toString()
var number: Int = -1
var name: String = ""
#Column(columnDefinition = "text")
var content: String? = ""
#Temporal(TemporalType.TIMESTAMP)
var createDate: Date = Date()
#ManyToOne
#JoinColumn(name = "novel_id")
#JsonIgnore
var novel: Novel = Novel();
constructor()
constructor(name: String, number: Int)
constructor(number: Int, name: String, content: String?, createDate: Date, novel: Novel) {
this.number = number
this.name = name
if (content != null) this.content = content
this.createDate = createDate
this.novel = novel
}
}
But when using the test function, it throws an error:
Caused by: org.springframework.data.mapping.PropertyReferenceException: No property test found for type Chapter!
at org.springframework.data.mapping.PropertyPath.lambda$new$0(PropertyPath.java:82) ~[spring-data-commons-2.0.0.M3.jar:na]
at java.util.Optional.orElseThrow(Optional.java:290) ~[na:1.8.0_111]
at org.springframework.data.mapping.PropertyPath.<init>(PropertyPath.java:82) ~[spring-data-commons-2.0.0.M3.jar:na]
at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:304) ~[spring-data-commons-2.0.0.M3.jar:na]
at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:284) ~[spring-data-commons-2.0.0.M3.jar:na]
at org.springframework.data.mapping.PropertyPath.from(PropertyPath.java:243) ~[spring-data-commons-2.0.0.M3.jar:na]
I search how to implement a custom repository, but I don't see the difference to my code?
Rename ChapterDaoImpl to ChapterRepositoryImpl.
Spring Data looks for custom implementations named after the Repository Interface + Impl.
You named the implementation based on the custom interface.

Implement your own object binder for Route parameter of some object type in Play scala

Well, I want to replace my String param from the following Play scala Route into my own object, say "MyObject"
From GET /api/:id controllers.MyController.get(id: String)
To GET /api/:id controllers.MyController.get(id: MyOwnObject)
Any idea on how to do this would be appreciated.
Well, I have written up my own "MyOwnObject" binder now. Another way of implementing PathBindable to bind an object.
object Binders {
implicit def pathBinder(implicit intBinder: PathBindable[String]) = new PathBindable[MyOwnObject] {
override def bind(key: String, value: String): Either[String, MyOwnObject] = {
for {
id <- intBinder.bind(key, value).right
} yield UniqueId(id)
}
override def unbind(key: String, id: UniqueId): String = {
intBinder.unbind(key, id.value)
}
}
}
Use PathBindable to bind parameters from path rather than from query. Sample implementation for binding ids from path separated by comma (no error handling):
public class CommaSeparatedIds implements PathBindable<CommaSeparatedIds> {
private List<Long> id;
#Override
public IdBinder bind(String key, String txt) {
if ("id".equals(key)) {
String[] split = txt.split(",");
id = new ArrayList<>(split.length + 1);
for (String s : split) {
long parseLong = Long.parseLong(s);
id.add(Long.valueOf(parseLong));
}
return this;
}
return null;
}
...
}
Sample path:
/data/entity/1,2,3,4
Sample routes entry:
GET /data/entity/:id controllers.EntityController.process(id: CommaSeparatedIds)
I'm not sure if it works for binding data in the path part of a URL, but you may want to read the docs on QueryStringBindable if you're able to accept your data as query params.

Implementing an indexer in a class in TypeScript

Is it currently possible to implement an indexer on a class in TypeScript?
class MyCollection {
[name: string]: MyType;
}
This doesn't compile. I can specify an indexer on an interface, of course, but I need methods on this type as well as the indexer, so an interface won't suffice.
Thanks.
You cannot implement a class with an indexer. You can create an interface, but that interface cannot be implemented by a class. It can be implemented in plain JavaScript, and you can specify functions as well as the indexer on the interface:
class MyType {
constructor(public someVal: string) {
}
}
interface MyCollection {
[name: string]: MyType;
}
var collection: MyCollection = {};
collection['First'] = new MyType('Val');
collection['Second'] = new MyType('Another');
var a = collection['First'];
alert(a.someVal);
This is an old question, for those looking for the answer: now it's possible to define a indexed property like:
let lookup : {[key:string]:AnyType};
the signature of the key must be either string or integer see:
Interfaces on www.typescriptlang.org
Is not possible to define an indexed property getter/setter in a class but you can "simulate" that in a way like this using Proxy:
class IndexedPropSample {
[name: string | symbol]: any;
private static indexedHandler: ProxyHandler<IndexedPropSample> = {
get(target, property) {
return target[property];
},
set(target, property, value): boolean {
target[property] = value;
return true;
}
};
constructor() {
return new Proxy(this, IndexedPropSample.indexedHandler);
}
readIndexedProp = (prop: string | symbol): any => {
return this[prop];
}
}
var test = new IndexedPropSample();
test["propCustom"] = "valueCustom";
console.log(test["propCustom"]); // "valueCustom"
console.log(test.readIndexedProp("propCustom")); // "valueCustom"
console.log(test instanceof IndexedPropSample); // true
console.log(Object.keys(test)); // ["propCustom", "readIndexedProp"]
you can try it in Typescript Playground