How to make post with data class - kotlin

I have this endpoint with this structure:
uri = http://127.0.0.1:9090/tables/mask
and this payload:
{
"_id" : "5d66c9b6d5ccf30bd5b6b541",
"connectionId" : "1967c072-b5cf-4e9e-1c92-c2b49eb771c4",
"name" : "Customer",
"columns" : [
{
"name" : "FirstName",
"mask" : true
},
{
"name" : "LastName",
"mask" : false
},
{
"name" : "City",
"mask" : false
},
{
"name" : "Phone",
"mask" : false
}
],
"parentId" : null
}
in my Kotlin code I have this structure to deserialize:
data class ColumnsMaskModel (val name:String, val mask:Boolean )
data class TablesMaskModel (val _id:String, val name:String, val connectionId:String, val columns:MutableList<ColumnsMaskModel?> )
and how can I use TablesMaskModel to make a HTTP post in Kotlin

You'll need an HTTP client to do that. Data classes themselves has nothing to do with HTTP, they are just data structures. There are a lot of HTTP clients available on Kotlin for JVM:
java.net.HttpURLConnection
Java 9's HttpClient
Apache HttpComponents
OkHttp
Ktor
Let's see how to make HTTP requests in Ktor:
data class ColumnsMaskModel(val name: String, val mask: Boolean)
data class TablesMaskModel(val _id: String, val name: String, val connectionId: String, val columns: MutableList<ColumnsMaskModel?>)
fun main() = runBlocking {
val client = HttpClient {
install(JsonFeature) {
serializer = JacksonSerializer()
}
}
val result = client.post<String> {
url("http://httpbin.org/post")
contentType(ContentType.Application.Json)
body = TablesMaskModel(
_id = "5d66c9b6d5ccf30bd5b6b541",
connectionId = "1967c072-b5cf-4e9e-1c92-c2b49eb771c4",
name = "Customer",
columns = mutableListOf(
ColumnsMaskModel(name = "FirstName", mask = true),
ColumnsMaskModel(name = "LastName", mask = false),
ColumnsMaskModel(name = "City", mask = false),
ColumnsMaskModel(name = "Phone", mask = false)
)
)
}
println(result)
client.close()
}
Note that Ktor uses suspending functions for HTTP requests, so you'll need a coroutine scope, runBlocking in this example.
Ktor supports various "backends" for HTTP clients – Apache, Coroutines IO, curl. It also has different "features" to enable on-the-flight payloads serializations and de-serializations, like in the example above.

Related

Adding dynamic maps in DynamoDB with Kotlin

I'm using Spring Boot, Kotlin and CrudRepository to add items to my Dynamo Table.
The map I'm trying to add is dynamic, and can change attributes every single time.
I add the date of the object (delta) and save it, but I am having several errors:
When I save:
#DynamoDBTable(tableName = "delta_computers_inventory")
class DeltaComputersInventory(
#DynamoDBHashKey
#DynamoDBAttribute(attributeName = "delta_computers_inventory_id")
var id: String = UUID.randomUUID().toString(),
#DynamoDBTyped(DynamoDBMapperFieldModel.DynamoDBAttributeType.M)
#DynamoDBAttribute(attributeName = "delta")
var delta: Map<String, Any?> = mapOf(),
) {
#DynamoDBAttribute(attributeName = "date")
var date: String = OffsetDateTime.now(ZoneOffset.UTC).format(
DateTimeFormatter.ISO_DATE_TIME
)
}
and I do:
.doOnSuccess { listOfDocuments ->
deltaComputersRepository.saveAll(
listOfDocuments.map {
DeltaComputersInventory(
delta = it,
)
}
)
}
I get:
reactor.core.Exceptions$ErrorCallbackNotImplemented: com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException: not supported; requires #DynamoDBTyped or #DynamoDBTypeConverted
instead, if I do it through an Item (Item.fromMap(it))
#DynamoDBTable(tableName = "delta_computers_inventory")
class DeltaComputersInventory(
#DynamoDBHashKey
#DynamoDBAttribute(attributeName = "delta_computers_inventory_id")
var id: String = UUID.randomUUID().toString(),
#DynamoDBTyped(DynamoDBMapperFieldModel.DynamoDBAttributeType.M)
#DynamoDBAttribute(attributeName = "delta")
var delta: Item = Item(),
) {
#DynamoDBAttribute(attributeName = "date")
var date: String = OffsetDateTime.now(ZoneOffset.UTC).format(
DateTimeFormatter.ISO_DATE_TIME
)
}
I get no error, but my item in my DynamoDB shows empty:
{
"delta_computers_inventory_id": {
"S": "d389d63e-8e93-4b08-b576-e37fae9a4d58"
},
"date": {
"S": "2023-01-24T12:00:33.620015Z"
},
"delta": {
"M": {}
},
}
What am I doing wrong?

Can I use optional parameter in path in Jetpack Compose Navigaiton?

I have this navigation graph
fun NavGraphBuilder.manageAvailabilityGraph() {
composable(
"availability/{id}",
arguments = listOf(
navArgument("id") {
type = NavType.StringType
nullable = true
},
),
) {
ManageAvailabilityScreen()
}
}
I thought I can use it for both
navHostController.navigate("availability")
navHostController.navigate("availability/123")
But first one does not work, I get
java.lang.IllegalArgumentException: Navigation destination that matches request NavDeepLinkRequest{ uri=android-app://androidx.navigation/availability } cannot be found in the navigation graph NavGraph(0x0) startDestination={Destination(0xcfbbf7da) route=home}
I fixed it by providing two different routes.
fun NavGraphBuilder.manageAvailabilityGraph() {
composable(
"availability",
) {
ManageAvailabilityScreen()
}
composable(
"availability/{id}",
arguments = listOf(
navArgument("id") {
type = NavType.StringType
nullable = true
},
),
) {
ManageAvailabilityScreen()
}
}
However, I want to know is if it is possible to combine both and just have one route with name "availability", so I don't need to repeat "availability"? And eseentially, both use the same screen.
I tried something like this but does not work.
fun NavGraphBuilder.manageAvailabilityGraph() {
composable(
"availability",
) {
ManageAvailabilityScreen()
navigation(startDestination = "{id}", "{id}") {
composable(
"{id}",
arguments = listOf(
navArgument("id") {
type = NavType.StringType
},
),
) {
ManageAvailabilityScreen()
}
}
}
}
You can mark id as the optional parameter in the route. Refer Documentation
fun NavGraphBuilder.manageAvailabilityGraph() {
composable(
"availability?id={id}",
arguments = listOf(
navArgument("id") {
type = NavType.StringType
nullable = true
},
),
) {
ManageAvailabilityScreen()
}
}
While navigating you can use,
navHostController.navigate("availability")
navHostController.navigate("availability?id=123")

Cannot upload file to ktor server using ktor client

I have setup server according to docs and try to upload file using code from this question:
val parts: List<PartData> = formData {
append(
"image",
InputProvider { ins.asInput() },
Headers.build {
this[HttpHeaders.ContentType] = "image/png"
this[HttpHeaders.ContentDisposition] = "filename=$name"
}
)
}
return HttpClient(Apache) {
install(Logging) {
logger = Logger.DEFAULT
level = LogLevel.ALL
}
}.submitFormWithBinaryData(formData = parts) {
url("$baseUrl/images")
}
If I use it as is (without request Content-Type), then server fails: "Content-Type header is required for multipart processing".
If I try to add header, client fails: "Header Content-Type is controlled by the engine and cannot be set explicitly".
Then it's actually something strange happening.
According to client logs, it's sending content type:
REQUEST: http://localhost:8090/images
METHOD: HttpMethod(value=POST)
COMMON HEADERS
-> Accept: */*
-> Accept-Charset: UTF-8
CONTENT HEADERS
BODY Content-Type: multipart/form-data; boundary=-675255df42a752ee167beaab-5799548c6088f411-a7e8dc449d68ab028c44d80-42b
BODY START
[request body omitted]
...
But on server side headers are completly different:
Accept-Charset [UTF-8]
Accept [*/*]
User-Agent [Ktor client]
Transfer-Encoding [chunked]
Host [localhost:8090]
Connection [Keep-Alive]
On other hand I can successfully upload file using okhttp (and headers actually matches):
val logging = HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger {
override fun log(message: String ) {
println(message)
}
})
logging.level = HttpLoggingInterceptor.Level.BODY
val client = OkHttpClient.Builder()
.addInterceptor(logging)
.build()
val file = File("image.png")
val part: MultipartBody.Part = MultipartBody.Part.Companion.createFormData(
"image",
"image.png",
file.asRequestBody("image/png".toMediaTypeOrNull())
)
val request = Request.Builder()
.url("http://localhost:8090/images")
.post(MultipartBody.Builder().addPart(part).build())
.build()
val res = client.newCall(request).execute()
res.body
Is it bug in ktor client or I missing something?
edit:
Both client and server versions is 1.4.1.
Corresponding gradle dependencies parts:
implementation("io.ktor:ktor-server-core:${ktor_version}")
implementation("io.ktor:ktor-server-netty:${ktor_version}")
implementation("io.ktor:ktor-jackson:$ktor_version")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.8")
...
implementation("io.ktor:ktor-client-core:$ktor_version")
implementation("io.ktor:ktor-client-cio:$ktor_version")
implementation("io.ktor:ktor-client-jackson:$ktor_version")
implementation("io.ktor:ktor-client-logging:$ktor_version")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.10.2")
route:
object ImagesRouter {
fun Routing.images(imagesModule: ImagesModule) {
route("images") {
get("/{id}") {
// ...
}
post {
val multipart = call.receiveMultipart() // fails here
// ...
}
}
}
}

read data from Map<String, Object>

I get these type of data from some api. I want to read data from "FK_User" which seems an object, when
I read data like this i get this error:
The method '[]' isn't defined for the class 'Object'.
- 'Object' is from 'dart:core'.
print(a["FK_User"]["username"]);
and the data is like this:
var a = {
"ID": "dummyID",
"FK_User": {
"username": "dummyID",
},
"Somefield": "dymmy",
}
var b = a["FK_User"]["username"];
how can I read this type of data?
Map<String, dynamic> a = {
"ID": "dummyID",
"FK_User": {
"username": "dummyID",
},
"Somefield": "dymmy",
};
var b = a["FK_User"]["username"]; // dummyID
Map<String,dynamic> a_map = Map.castFrom(a);
Map<String,dynamic> fk_user_map = Map.castFrom(a_map["FK_user"]);

How to get Multiple Key Value in Dart Http

Please How Can I get this kind of API response in Flutter using http with FutureBuilder.
"GLODATA": {
"1000": {
"pname": "GLO 1.0GB/14days",
"price": "470",
"pld": "1000"
},
"1600.01": {
"pname": "GLO 2.0GB/30days",
"price": "940",
"pld": "1600.01"
},
"3750.01": {
"pname": "GLO 4.5GB/30days",
"price": "1900",
"pld": "3750.01"
},
"5000.01": {
"pname": "GLO 7.2GB/30days",
"price": "2430",
"pld": "5000.01"
}
},
I think in your case, you will need to do something like this:
Api:
Future<http.Response> getData() async {
final _api = "http://yourendpointhere";
http.Response response = await http.get(_api);
if (response.statusCode != 200) {
throw Exception("Request failed...");
}
return response;
}
Then consume your api:
http.Response response = await _apiInstance.getData();
if (response.body != null && response.body.isNotEmpty) {
String source = Utf8Decoder().convert(response.bodyBytes);
Map<String, Map<String, dynamic>> data = Map();
data = Map<String, Map<String, dynamic>>.from(json.decode(source));
}
After that, you can create a factory constructor in your model class, receiving that map and turning it into an instance of your class.