I have a problem returning data class and handling it in the frontend. I dont know if the problem relies in the backend or the frontend
#RestController
#CrossOrigin
#RequestMapping(path = ["/1/private/profile"])
class ProfileController {
...OTHER CODE...
#GetMapping(
path = ["/getUser"],
consumes = [MediaType.APPLICATION_JSON_VALUE],
produces = [MediaType.APPLICATION_JSON_VALUE])
fun getUser(#RequestHeader(AUTHORIZATION) token: String): User? {
val uid = token.getUid()
val user = userRepository?.findItemByUid(uid)
user?.let {
return user
}?: kotlin.run {
return null
}
}
}
This is the controller holding the endpoint called from the frontend. The endpoint is returning 200 when called from the frontend. But the body returned is completely wrong.
response body: {"id":{"timestamp":1672696689,"dateā¦y":"Sweden","uid":"4yRhpe43hHUwDGkqMqVBqX3G60p2"}
But expected is json string from the data class below
#Document("users")
data class User(
val id: ObjectId = ObjectId.get(),
val fullName: String,
val email: String,
val country: String,
val uid: String
)
What could i be doing wrong? It works perfectly if the endpoint for some reason returns a list of objects.
Related
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.
I have a Model which conforms to Content:
import Vapor
import Fluent
final class User: Model, Content {
static let schema = "user"
#ID(key: .id)
var id: UUID?
#Field(key: "email")
var email: String
init() { }
init(id: UUID? = nil, email: String) {
self.id = id
self.email = email
}
}
This can be fetched from the database and directly returned from a route, so the client receives the users as JSON.
Now I want to add an additional property to the user, which isn't stored in the database but a fixed value or just added after the user is loaded from the database:
final class User: Model, Content {
// ...
var someOther: String = "hello"
// ...
}
or
final class User: Model, Content {
// ...
var someOther: String? // set later
// ...
}
Now the problem is that this property is never added to the JSON, so the client only gets the user's id and email and NOT the someProperty. How do I fix this?
When you want to send or receive data from your client that has a different representation from what your model has, it's common practice to define a custom type that you can then convert to and from the model type.
Given this, you would define a new User.Response type that conforms to Content instead of the User model, and return that from your route instead.
extension User {
struct Response: Content {
let id: UUID
let email: String
let otherValue = "Hello World"
}
}
func user(request: Request) throws -> EventLoopFuture<User.Response> {
return User.find(UUID(), on: request.db).flatMapThrowing { user in
guard let id = user.id else {
throw Abort(.internalServerError, "User is missing ID")
}
return User.Response(id: id, email: user.email)
}
}
The reason the additional value that you added wasn't encoded from your model is because the default encoder for model types iterates over the field properties and encodes those properties, so if you have a property that doesn't use one of the ID, Field, Parent, Children, or other field property wrappers, it won't be encoded.
While learning how to use Swift and Vapor to create a REST api, while testing this API using postman, I am unable to make an API Post request, the error I receive is:
{
"error": true,
"reason": "Value of type 'String' required for key 'email'."
}
I am using a PostgreSQL database reference the model. I feel something is wrong with my model.
Here is the model I am using:
final class Todo: PostgreSQLModel {
var id: Int?
var email: String
var password: String
var name_first: String
var name_last: String
var username: String
init(id: Int?, email: String, name_first: String, name_last: String, username:String, password: String) {
self.id = id
self.email = email
self.name_first = name_first
self.name_last = name_last
self.username = username
self.password = password
}
}
It turns out I was using postman wrong. I was passing things through the header the body my mistake.
I declare a model in ingredient.model.ts
export class Ingredient {
constructor(private name: string, public amount: number) {}
getName() { return this.name }
}
In ingredients.service.ts, if I get them in this way:
httpClient.get<Ingredient>(url).subscribe(
(igredient) => {
console.log(igredient.getName());
});
It gives errors in console, such as "no method getName in property igredient".
Also, whenever I try to declare a property type Category[] it fails, but Array seems working fine.
Edit:
I want to provide more info.
Given the Igredient model and the following JSON structure:
{
name: "Apple",
amount: "5",
created_at: "date",
}
The Igredient constructor isn't even invoked, therefore the GET payload won't be parsed.
You'll need to use a property, not a method. The returned object is really a json object, and there is no such thing as "getName()" method (despite your effort to add a type information). Try something like this:
export interface Ingredient {
strin: string,
amount: number,
created_at: string
}
httpClient.get<Ingredient>(url).subscribe(
(igredient) => {
console.log(igredient.amount);
});
EDIT: You need to provide a type information based on the expected json object. If the returned json object has attributes, strin, amount, and created_at, then you need to define a type that is compatible with the expected json object.
In angular 5, You can do this:
export interface Deserializable<T> {
deserialize(input: any): T;
}
export class Ingredient implments Deserializable<Ingredient>{
constructor(private name: string, public amount: number) {}
deserialize(input: any): Project {
Object.assign(this, input);
// do nested thing here -pop arrays of nested objects and create them
}
return this;
}
now in your service:
httpClient.get<Ingredient>(url).pipe(map(elem=>this.foo(elem)))
.subscribe((igredient) => {console.log(igredient.getName());
});
foo(ingredient:Ingrdient){
var i = new Ingridiant().desrialize(ingredient)
}
after the map you will have the Ingradient class, not the object.
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.