How to render partial responses with Akka HTTP - akka-http

I would like to render partial response using Akka HTTP. In the request the client should specify which fields to include in the response (e.g. using a fields request parameter, for example: https://www.acme.com/api/users/100?fields=id,name,address).
I would appreciate any pointers on how to solve this.

Akka http provides a useful DSL known as Directives to solve these types of problems. You can match on a particular path and then extract the HttpRequest query string for the "fields" key:
import akka.http.scaladsl.server.Directives._
val route = get {
path("api" / "users" / IntNumber) { pathInt =>
parameter('fields) { fields =>
complete(generateResponse(pathInt, fields))
}
}
}
For the given example request ("https://www.acme.com/api/users/100?fields=id,name,address") the generateResponse function will be called with 100 and id,name,address as the input variables. Say you have some lookup table of the values:
case class Person(id : String, name : String, address : String, age : Int)
val lookupTable : Map[Int, Person] = ???
You can then use this lookup table to get the person and extract the appropriate fields:
def personField(person : Person)(field : String) = field match {
case "id" => s"\"id\" = \"${person.id}\""
case "name" => s"\"name\" = \"${person.name}\""
...
}
//generates JSON responses
def generateResponse(key : Int, fields : String) : String = {
val person = lookupTable(key)
"{ " +
fields
.split(",")
.map(personField(person))
.reduce(_ + " " + _)
+ " }"
}

Related

How to set dynamic value as a key to json string in request

In the below JSON request: value for "paid" is dynamically generated. When I try to pass the value to JSON string attribute, instead of dynamic value to replace "paid" it is taking "paid" as a string.
{
"ENCRYP": {
"data": "ENCRYPTED",
"tEncryptedP": {
"#(paid)": {
"encryptedP": "#(parameters)"
}
}
}
}
How to pass a dynamic value to JSON string attribute in this case?
It looks like you are trying to set a dynamic key using an embedded expression,
not sure if it is possible to set a new key using an embedded expression.
but you can try something like this,
* def paid = "PaidKey"
* def parameters = "PAID PARAMETERS"
* def enc =
"""
{
"ENCRYP": {
"data": "ENCRYPTED",
"tEncryptedP": {
}
}
}
"""
* def encryptedP = {"encryptedP": "#(parameters)"}
* eval enc.ENCRYP.tEncryptedP[paid] = encryptedP

Define `type alias` with a partial definition or default

Is there a way to DRY this?
I don’t want to repeat contents : List Nav and wrapperId : String
type alias InputModel =
{ contents : List Nav
, containerClassName : Maybe String
, wrapperId : String
}
type alias Model =
{ contents : List Nav
, containerClassName : String
, wrapperId : String
}
modelWithDefaults : InputModel -> Model
modelWithDefaults input =
{ input | containerClassName = withDefault "" input.containerClassName }
Yes there is! You can move the common fields into a separate record and add a row variable to it. The row variable, a, which specify the remaining fields, can then be supplied later:
type alias CommonModel a =
{ a
| contents : List Nav
, wrapperId : String
}
type alias InputModel =
CommonModel
{ containerClassName : Maybe String }
type alias Model =
CommonModel
{ containerClassName : String }
You can also use row variables to write functions that accept any record as long as it has the common fields. E.g.
getWrappedId : CommonModel a -> String
getWrapperId { wrapperId } = wrappedId
will accept both InputModel and Model, or any other record that contains at least the fields specified by CommonModel. The row variable will be inferred, just like any other type variable would.

How to add multiple filter conditions based on incoming parameters to the Exposed DAO API ".find"

I am writing a dropwizard application using kotlin and one of the resource methods will receives multiple parameters(email, phone, is_deleted, etc). Based on the incoming query params, I have to filter the table. I am using DAO API of Exposed to construct the query.
val result = User.find { //SqlExpressionBuilder
Users.isDeleted eq false
Users.email eq "so#soso.com"
}.sortedByDescending { it.createdAt }
How to add multiple where conditions to the query based on the map of query params using a for in loop?
I had to solve the problem using DSL. Seems like DAO doesn't have such provision to create such adhoc query.
val query = Users.selectAll()
for((k, v) in params) {
val value = URLDecoder.decode(v, "UTF-8")
when(value) {
"email" -> query.andWhere { Users.email eq value }
"phone" -> query.andWhere { Users.phone eq value }
}
}
return query.andWhere { Users.isDeleted.eq(false) }
As #Tapac mentioned, one can use User.wrapRows(query).sortedByDescending() also to frame such query but again one have to use DSL.
Hope that helps someone.
It is possible:
val email = URLDecoder.decode("email", "UTF-8")
val phone = URLDecoder.decode("phone", "UTF-8")
Users.find {
if (email != null) (Users.email eq email) else Op.TRUE
.and(if (phone != null) (Users.phone eq phone) else Op.TRUE)
}

RavenDB creating static index and query on dictionary

I'm trying to create a static index where I want all documents where a key exists and has a value. The value itself is not important, only the key exists.
I'm exploring this example with dynamic fields:
https://ravendb.net/docs/article-page/2.5/csharp/client-api/advanced/dynamic-fields
... and although I'm getting the index to work, I'm not sure if the query I'm using is correct.
This is the sample class:
public class Result
{
public Dictionary<string, List<Data>> Results { get; set; }
}
The key in the dictionary is the ID of a user (for example "user/1") and the value is a list of data-objects. The so the json-structure looks like this:
{
"Results" :
{
"user/1": [{...}],
"user/2": [{...}],
}
}
The index I use is this:
public class Result_ByUserId : AbstractIndexCreationTask<Result>
{
public Result_ByUserId()
{
Map = res => from r in res
select new
{
_ = r.Results
.Select(d => CreateField(d.Key, d.Value))
};
}
}
My problem comes down to the query, as it assumes I want to look at a specific key and value.
var resultat = session.Advanced.DocumentQuery<Result>("Result/ByUserId ")
.WhereEquals("user/1", "") // How do I write a !isNullOrEmpty?
.ToList();
... which I don't want to do. I only want the results that has a key in which the value is not null or empty. Does anybody have any good tips?
What you can do is index a boolean flag depending on if the dictionary has a value or not and then query on that.
public class Result_ByUserId : AbstractIndexCreationTask<Result>
{
public Result_ByUserId()
{
Map = res => from r in res
select new
{
_ = r.Results
.Select(d => CreateField(d.Key, d.Value != null ? true : false, false, true))
};
}
}
The query can then be:
var resultat = session.Advanced.DocumentQuery<Result>("Result/ByUserId ")
.WhereEquals("user/1", true)
.ToList();
This will return any Result documents that has a Dictionary with a key of user/1 and a dictionary value that's not null.
Not sure it's the best way of doing it, but it worked for me...
Hope this helps!

How to Retrieve the Primary Key When Saving a New Object in Anorm

I'm using Scala Play! framework with Anorm to persist the data model to the database. I followed the example code here :
case class Bar(id: Pk[Long], name: String)
object Bar {
val simple = {
get[Pk[Long]]("id") ~
get[String]("name") map {
case id~name => Bar(id, name)
}
}
def findAll(): Seq[Bar] = {
DB.withConnection { implicit connection =>
SQL("select * from bar").as(Bar.simple *)
}
}
def create(bar: Bar): Unit = {
DB.withConnection { implicit connection =>
SQL("insert into bar(name) values ({name})").on(
'name -> bar.name
).executeUpdate()
}
}
}
Trying to expand on it, I want to retrieve the primary key just created and store it in the case class.
How can I retrieve the primary key?
Use the executeInsert method instead of executeUpdate. Noted here, the foremer method returns Option[T] where T is the type of the primary key.
You can extract the value with a match statement:
DB.withConnection { implicit connection =>
SQL(...).executeInsert()
} match {
case Some(long) => long // The Primary Key
case None => ...
}