New to akka-http - akka-http

I am new to akka-http. I am using this in my project. I am not able to understand what exactly marshalling and unmarshalling is.
If someone can explain it with a brief example showing how to marshall and unmarshall json.

When you receive a request the in http it is in wire format i.e byte string, Unmarshaling is converting this byte string to higher level format, on the flip side is marshaling were you convert to low level format.
Example in akka-http of converting json string(str) to case class(person):
case class Person(name: String, age: Int)
val str = """{"name": "some", "aga": 10}"""
impicit val jsonF = jsonFormat2(Person)
val person = JsonParser(str).convertTo[Person]
But a better approach is using the entity directive from akka-http:
val route = post {
entity(as[Person]) { person =>
complete(s"Person: ${person.name} - favorite number: ${person.favoriteNumber}")
}
}
The example from the documentation here
by the way you need the implicit formater in your scope for unmarshaling to succeed and the number to match the number of field in your case class.

Related

Kotlin (Ktor) request class for multipart form data

I want to create a request class that collects all the parts (files and items) and validate it, something similar to the example I put (below) with the json requests.
REQUEST JSON SERIALIZABLE EXAMPLE CLASS
import kotlinx.serialization.Serializable
#Serializable
class CreateGroupRequest(
val name: String,
val description: String? = null,
val visibility: String? = "PUBLIC"
)
HANDLE JSON REQUEST EXAMPLE
route("create") {
post {
val request = call.receive<CreateGroupRequest>()
try {
//CODE
call.respond(HttpStatusCode.OK)
} catch (e: SharedDomainException) {
call.respond(HttpStatusCode(e.errorCode, e.errorMessage))
}
}
}
What I mean, for example, is that in this case I want to change it because the groups also has a profile photo that I want to upload or in other cases, posts domain has text, author and a multiple images.
I have read this stackOverflow post but I can't see how I can make a general class to read the multipart requests without having to duplicate code in each handler.
So, does anyone know how I can read the request multipart-form-data body in a shared class and validate it with kotlin/ktor?
In principle, you can use the ContentNegotiation and register a content converter for the multipart/form-data Content-type. In the convertForReceive method you can use CIOMultipartDataBase to parse multipart data and then deserialize it using kotlinx.serialization library. For deserialize method call you need to provide a decoder for the MultiPartData objects, which you need to implement.
The above approach will work but is very inefficient for parts with a large binary body because parts in an HTTP message go one after another so all of them will be eagerly read into memory.

Extracting Nested POJO Object with Rest-Assured

I'm writing some tests using rest-assured and its Kotlin extensions to test some simple Spring MVC endpoints. I'm trying to understand how to extract values.
One endpoint returns a BookDetailsView POJO, the other returns a Page<BookDetailsView> (where Page is an interface provided by Spring for doing paging).
BookDetailsView is a really simple Kotlin data class with a single field:
data class BookDetailsView(val id: UUID)
For the single object endpoint, I have:
#Test
fun `single object`() {
val details = BookDetailsView(UUID.randomUUID())
whenever(bookDetailsService.getBookDetails(details.id)).thenReturn(details)
val result: BookDetailsView = Given {
mockMvc(mockMvc)
} When {
get("/book_details/${details.id}")
} Then {
statusCode(HttpStatus.SC_OK)
} Extract {
`as`(BookDetailsView::class.java)
}
assertEquals(details.id, result.id)
}
This works as expected, but trying to apply the same technique for the Page<BookDetailsView> runs afoul of all sorts of parsing challenges since Page is an interface, and even trying to use PageImpl isn't entirely straightforward. In the end, I don't even really care about the Page object, I just care about the nested list of POJOs inside it.
I've tried various permutations like the code below to just grab the bit I care about:
#Test
fun `extract nested`() {
val page = PageImpl(listOf(
BookDetailsView(UUID.randomUUID())
))
whenever(bookDetailsService.getBookDetailsPaged(any())).thenReturn(page)
val response = Given {
mockMvc(mockMvc)
} When {
get("/book_details")
} Then {
statusCode(HttpStatus.SC_OK)
body("content.size()", `is`(1))
body("content[0].id", equalTo(page.first().id.toString()))
} Extract {
path<List<BookDetailsView>>("content")
}
println(response[0].javaClass)
}
The final println spits out class java.util.LinkedHashMap. If instead I try to actually use the object, I get class java.util.LinkedHashMap cannot be cast to class BookDetailsView. There are lots of questions and answers related to this, and I understand it's ultimately an issue of the underlying JSON parser not knowing what to do, but I'm not clear on:
Why does the "simple" case parse without issue?
Shouldn't the type param passed to the path() function tell it what type to use?
What needs configuring to make the second case work, OR
Is there some other approach for grabbing a nested object that would make more sense?
Digging a bit into the code, it appears that the two cases may actually be using different json parsers/configurations (the former seems to stick to rest-assured JSON parsing, while the latter ends up in JsonPath's?)
I don't know kotlin but here is the thing:
path() doesn't know the Element in your List, so it'll be LinkedHashMap by default instead of BookDetailsView.class
to overcome it, you can provide TypeReference for this.
java example
List<BookDetailsView> response = ....then()
.extract().jsonPath()
.getObject("content", new TypeRef<List<BookDetailsView>>() {});
kotlin example
#Test
fun `extract nested`() {
var response = RestAssured.given().get("http://localhost:8000/req1")
.then()
.extract()
.jsonPath()
.getObject("content", object : TypeRef<List<BookDetailsView?>?>() {});
println(response)
//[{id=1}, {id=2}]
}

HOCON config file dynamic substitution

I'm using HOCON to configure log messages and I'm looking for a way to substitute placeholder values dynamically.
I know that ${?PLACEHOLDER} will read an environment variable and returns an empty string when the PLACEHOLDER environment variable doesn't exist.
Example
This is an example of what I had in mind:
(I'm using config4k to load HOCON )
data class LogMessage(val message: String, val code: String)
fun getMessage(key: String, placeholderValues: Array<String> = arrayOf()): LogMessage {
val config = ConfigFactory.parseString("MY_LOG_MESSAGE {code = ABC-123456, message = My log message with dynamic value %0% and another dynamic value %1% }")
val messageObject = config.extract<LogMessage>(key)
var message = messageObject.message
placeholderValues.forEachIndexed { placeholderIndex, value ->
message = message.replace("%$placeholderIndex%", value)
}
return messageObject.copy(message = message)
}
fun main(args: Array<String>) {
println(getMessage("MY_LOG_MESSAGE", arrayOf("value 1", "value 2")))
// prints: LogMessage(message=My log message with dynamic value value 1 and another dynamic value value 2, code=ABC-123456)
}
Even though this works, it doesn't look like the best solution and I assume there already is a solution for this.
Could someone tell me if there is a built-in solution?
First things first.
HOCON is just a glorified JSON format.
config4k is just a wrapper.
All your work is being done by Typesafe Config, as you've probably noticed.
And judging by their documentation and code, they support placeholders only from withing the file, or from the environment:
This library limits itself to config files. If you want to load config
from a database or something, you would need to write some custom
code.
But for what you're doing, simple String.format() should be enough:
fun interpolate(message: String, vararg args: Any) = String.format(message, *args)
println(interpolate("My message was %s %s %s %s", "a", 1, 3.32, true))
// My message was a 1 3.32 true
Notice that you can use * to destructure your array.

Elm type confusion

I started on my first, simple web app in Elm. Most of my code is currently adapted from https://github.com/rtfeldman/elm-spa-example. I am working against a API that will give me a authToken in the response header. I have a AuthToken type that is supposed to represent that token. Taking the value out of the header and converting it to a result that's either a error String or a AuthToken is causing trouble. I expected that I could just say I am returning a AuthToken, return a String and it would be fine because my AuthTokens right now are just Strings. It seems like there clearly is something about Elm types I am not understanding.
Here is the definition of AuthToken:
type AuthToken
= AuthToken String
and my way too complicated function that for now just tries to do some type changes (later I want to also do work on the body in here):
authTokenFromHeader : String -> Http.Response String -> Result String AuthToken
authTokenFromHeader name resp =
let
header = extractHeader name resp
in
case header of
Ok header ->
let
token : Result String AuthToken
token = Ok (AuthToken header)
in
token
Err error -> Err error
I expected the happy case would return a Ok result with the string from the response header converted to a AuthToken as its value. Instead I am getting Cannot find variable 'AuthToken'. From the documentation I expected to get a constructor with the same name as the type. If I just use Ok header, the compiler is unhappy because I am returning Result String String instead of the promised Result String AuthToken.
What's the right approach here?
The code looks fine as is. The error message indicates that type AuthToken has been defined in a different module and not imported completely to the module that defines authTokenFromHeader. You can read about Elm's module system in the Elm guide: Modules.
A possible fix, assuming that type AuthToken is defined in module Types, and authTokenFromHeader is defined in module Net, is:
Types.elm:
module Types exposing (AuthToken(..))
type AuthToken = AuthToken String
Net.elm:
module Net exposing (authTokenFromHeader)
import Types exposing (AuthToken(..))
authTokenFromHeader : String -> Http.Response String -> Result String AuthToken
authTokenFromHeader name resp =
...
Note the use of AuthToken(..) instead of just AuthToken, which ensures that the type as well as the type constructors are imported/exported.
Or just move the definition of type AuthToken into the same file as the definition of authTokenFromHeader.

f# - how to serialize option and DU as value or null (preferably with json.net)

How can i get my json from web api to format only value or null for Option types and Discriminated Unions preferably using Newtonsoft.
I am currently using Newtonsoft and only have to add this to web api for it to work:
config.Formatters.JsonFormatter.SerializerSettings <- new JsonSerializerSettings()
When i consume the data on my side, i can easily convert it back to an F# item using: JsonConvert.DeserializeObject<'a>(json)
The api will be consumed by NON .NET clients as well so i would like a more standard formatted json result.
I would like to to fix my issue, w/o having to add code or decorators to all of my records/DU in order for it to work. I have lots of records with lots of properties, some are Option.
ex (this is how DU is serializing):
// When value
"animal": {
"case": "Dog"
}
// When no value
"animal": null
This is what I need:
// When value
"animal": "Dog"
// When no value
"animal": null
This is how an Option type is serializing:
"DocumentInfo": {
"case": "Some",
"fields": [
{
"docId": "77fb9dd0-bfbe-42e0-9d29-d5b1f5f0a9f7",
"docType": "Monkey Business",
"docName": "mb.doc",
"docContent": "why cant it just give me the values?"
}
]
}
This is what I need:
"DocumentInfo": {
"docId": "77fb9dd0-bfbe-42e0-9d29-d5b1f5f0a9f7",
"docType": "Monkey Business",
"docName": "mb.doc",
"docContent": "why cant it just give me the values?"
}
Thank you :-)
You could try using Chiron. I haven't used it myself so I can't give you an extensive example, but https://neoeinstein.github.io/blog/2015/12-13-chiron-json-ducks-monads/index.html has some bits of sample code. (And see https://neoeinstein.github.io/blog/2016/04-02-chiron-computation-expressions/index.html as well for some nicer syntax). Basically, Chiron knows how to serialize and deserialize the basic F# types (strings, numbers, options, etc.) already, and you can teach it to serialize any other type by providing that type with two static methods, ToJson and FromJson:
static member ToJson (x:DocumentInfo) = json {
do! Json.write "docId" x.docId
do! Json.write "docType" x.docType
do! Json.write "docName" x.docName
do! Json.write "docContent" x.docContent
}
static member FromJson (_:DocumentInfo) = json {
let! i = Json.read "docId"
let! t = Json.read "docType"
let! n = Json.read "docName"
let! c = Json.read "docContent"
return { docId = i; docType = t; docName = n; docContent = c }
}
By providing those two static methods on your DocumentInfo type, Chiron will automatically know how to serialize a DocumentInfo option. At least, that's my understanding -- but the Chiron documentation is sadly lacking (by which I mean literally lacking: it hasn't been written yet), so I haven't really used it myself. So this may or may not be the answer you need, but hopefully it'll be of some help to you even if you don't end up using it.
I have found the solution that allows me to use Newtonsoft (JSON.NET), apply custom converters for my types where needed and not require any changes to my DU's or Records.
The short answer is, create a custom converter for Json.Net and use the Read/Write Json overrides:
type CustomDuConverter() =
inherit JsonConverter() (...)
Unfortunately the ones I have found online that were already created doesn't work as is for my needs listed above, but will with slight modification. A great example is to look at: https://gist.github.com/isaacabraham/ba679f285bfd15d2f53e
To apply your custom serializer in Web Api for every call, use:
config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new CustomDuConverter())
To deserialize use (example that will deserialize to DU):
JsonConvert.DeserializeObject<Animal>("Dog", customConverter)
ex:
type Animal = Dog | Cat
json:
"animal": "Dog"
This will allow you to create a clean Api for consumers and allow you to consume 3rd party Json data into your types that use Option, etc.