Forward request body to response in Actix-Web - asynchronous

I would like to forward the Actix-Web request body to the response body (something like echo) but it gives a mismatched types error.
use actix_web::*;
use futures::future::ok;
use futures::Future;
fn show_request(
request: &actix_web::HttpRequest
) -> Box<Future<Item=HttpResponse, Error=Error>> {
request
.body()
.from_err::<actix_web::error::PayloadError>()
.map(move |f| {
Box::new(ok(actix_web::HttpResponse::Ok()
.content_type("text/plain")
.body(f)))
})
}
pub fn index(scope: actix_web::Scope<()>) -> actix_web::Scope<()> {
scope.handler("", |req: &actix_web::HttpRequest| {
show_request(req)
})
}
fn main() {
actix_web::server::new(|| {
vec![
actix_web::App::new()
.scope("", index)
.boxed(),
]
}).bind("127.0.0.1:8000")
.expect("Can not bind to port 8000")
.run();
}
[package]
name = "temp"
version = "0.1.0"
authors = ["John"]
edition = "2018"
[dependencies]
actix-web = "0.7"
futures = "0.1"
error:
error[E0308]: mismatched types
--> src/proj.rs:50:2
|
49 | ) -> Box<Future<Item=HttpResponse, Error=Error>> {
| ------------------------------------------- expected `std::boxed::Box<(dyn futures::Future<Error=actix_web::Error, Item=actix_web::HttpResponse> + 'static)>` because of return type
50 | request
| _____^
51 | | .body()
52 | | .from_err::<actix_web::error::PayloadError>()
53 | | .map(move |f| {
... |
56 | | .body(f)))
57 | | })
| |__________^ expected struct `std::boxed::Box`, found struct `futures::Map`
|
= note: expected type `std::boxed::Box<(dyn futures::Future<Error=actix_web::Error, Item=actix_web::HttpResponse> + 'static)>`
found type `futures::Map<futures::future::FromErr<actix_web::dev::MessageBody<actix_web::HttpRequest>, actix_web::error::PayloadError>, [closure#src/.rs:53:8: 57:4]>`
Why does this error occur and how can I fix it?

You are trying to return a Future without Boxing, you are Boxing the response in Map's closure, not the expected Future. Using futures::future::ok is not necessary because your request.body is already future.
fn show_request(
request: &actix_web::HttpRequest,
) -> Box<Future<Item = HttpResponse, Error = Error>> {
Box::new(request.body().map_err(|e| e.into()).map(move |f| {
actix_web::HttpResponse::Ok()
.content_type("text/plain")
.body(f)
}))
}

Related

How to properly use Partition and Sort Key

I know how to use the PK and SK but in my case it's a bit different.
I basically pass some distance and I want to retrieve the vehicles that can be used.
e.g:
HTTP Request passing the distance e.g. 50km so (bycicle, car and train need to be returned)
Basically I want to retrieve the vehicles that can be used
PK | distanceMin (SK) | distanceMax | vehicleName
distance | 0 | 10 | bycicle
distance | 10 | 500 | car // cannot have SK as 0
distance | 50 | 9999 | train // cannot have SK as 0
// This will have thousands of records
The problem is that I need to do scans for this and it's cost expensive and I also cannot use the same SK (0) for car and train because it would be a duplicate.
How can I pass a distance and make a query to get all the vehicles that are inside the range distanceMin and distanceMax?
My current code
searchByDistance: async function(distance) {
const search = parseInt(distance);
let params = {
TableName: this.myTable,
ScanIndexForward: true,
ConsistentRead: false,
KeyConditionExpression: 'PK=:pkKey AND distanceMin <= :search',
ProjectionExpression: 'vehicleName',
ExpressionAttributeValues: {
':pkKey': 'distance',
':search': search,
},
FilterExpression: 'distanceMax >= :search',
};
let scanResults = [];
let items;
try {
do {
items = await this.getDbClient().query(params).promise();
(items.Items || []).forEach((item) => scanResults.push(item));
params.ExclusiveStartKey = items.LastEvaluatedKey;
} while (typeof items.LastEvaluatedKey !== 'undefined');
} catch (err) {
console.log(err, err.stack);
}
return scanResults;
},

Why is is the compiler telling me "Type misMatch for App message" when they are the same type

So, I've been fighting with the compiler on a type error.
This code was working as of a couple days ago.
Type misMatch for App level message
App.fs snippets
module App =
type Msg =
| ConnectionPageMsg of ConnectionPage.Msg
| CodeGenPageMsg of CodeGenPage.Msg
//...
let update (msg : Msg) (model : Model) =
match msg with
| ConnectionPageMsg msg ->
let m, cmd = ConnectionPage.update msg model.ConnectionPageModel
{ model with ConnectionPageModel = m }, cmd
| CodeGenPageMsg msg ->
let m, cmd = CodeGenPage.update msg model.CodeGenPageModel
{ model with CodeGenPageModel = m }, cmd
//...
let runner =
Program.mkProgram init update view
|> Program.withConsoleTrace
|> XamarinFormsProgram.run app
I've added explicit aliases and the original error :
Type mismatch. Expecting a
'App.Msg -> App.Model -> App.Model * Cmd<App.Msg>'
but given a
'App.Msg -> App.Model -> App.Model * Cmd<Msg>'
The type 'App.Msg' does not match the type 'Msg'
Became these:
App.fs(50,50): Error FS0001: The type 'PocoGen.Page.ConnectionPage.Msg' does not match the type 'PocoGen.Page.CodeGenPage.Msg' (FS0001) (PocoGen)
App.fs(32,32): Error FS0001: Type mismatch.
Expecting a 'App.Msg -> App.Model -> App.Model * Cmd<App.Msg>'
but given a 'App.Msg -> App.Model -> App.Model * Cmd<Msg>'
The type 'App.Msg' does not match the type 'Msg' (FS0001) (PocoGen)
Other remarks
Right before these errors started appearing I was working on converting a blocking syncronous call to a async command in the ConnectionTestPage and removed the calling code for the cmd hoping that would fix it. (It did not)
ConnectionPage.fs Messages
type Msg =
| UpdateConnectionStringValue of string
| UpdateConnectionStringName of string
| TestConnection
| TestConnectionComplete of Model
| SaveConnectionString of ConnectionStringItem
| UpdateOutput of string
ConnectionPage.fs update
let update (msg : Msg) (m : Model) : Model * Cmd<Msg> =
match msg with
| UpdateConnectionStringValue conStringVal ->
{ m with
ConnectionString =
{ Id = m.ConnectionString.Id
Name = m.ConnectionString.Name
Value = conStringVal }
CurrentFormState =
match hasRequredSaveFields m.ConnectionString with
| false -> MissingConnStrValue
| _ -> Valid }, Cmd.none
| UpdateConnectionStringName conStringName ->
{ m with
ConnectionString =
{ Id = m.ConnectionString.Id
Name = conStringName
Value = m.ConnectionString.Value }
CurrentFormState =
match hasRequredSaveFields m.ConnectionString with
| false -> MissingConnStrValue
| _ -> Valid }, Cmd.none
| UpdateOutput output -> { m with Output = output }, Cmd.none
| TestConnection -> m, Cmd.none
| TestConnectionComplete testResult -> { m with Output = testResult.Output + "\r\n" }, Cmd.none
| SaveConnectionString(_) -> saveConnection m, Cmd.none
I've played with the Fsharp Version (because incidentally I did update to 4.7.2 a bit before getting this error)
The Full Repo:
https://github.com/musicm122/PocoGen_Fsharp/tree/master/PocoGen
The two branches of the match inside App.update have different types. The first branch has type App.Model * Cmd<ConnectionPage.Msg> and the second page has type App.Model * Cmd<CodeGenPage.Msg>.
You can't generally do that. This, for example, wouldn't compile:
let x =
match y with
| true -> 42
| false -> "foo"
What type is x here? Is it int or is it string? Doesn't compute. A match expression has to have all branches of the same type.
To convert Cmd<ConnectionPage.Msg> into a Cmd<App.Msg> (by wrapping the message in ConnectionPageMsg) you can use Cmd.map:
let update (msg : Msg) (model : Model) =
match msg with
| ConnectionPageMsg msg ->
let m, cmd = ConnectionPage.update msg model.ConnectionPageModel
{ model with ConnectionPageModel = m }, Cmd.map ConnectionPageMsg cmd
| CodeGenPageMsg msg ->
let m, cmd = CodeGenPage.update msg model.CodeGenPageModel
{ model with CodeGenPageModel = m }, Cmd.map CodeGenPageMsg cmd

Is there a way to check nested option values in one pattern in F#?

Let's pretend we have the following types:
type Message {
text : Option<string>
}
type Update {
msg : Option<Message>
}
How do I match it in one line, like in C# using null-conditional operator i.e update?.msg?.text ?
Is there a way to do it like this?:
match msg, msg.text with
| Some msg, Some txt -> ...
| None -> ...
because I don't want to be writing 2 nested match expressions.
You have two Record types (missing the "=" in your example). To match some variable of Update type, you could do as follows:
type Message = { text : Option<string> }
type Update = { msg : Option<Message> }
let u = {msg = Some({text = Some "text"})}
//all 3 possible cases
match u with
| {msg = Some({text = Some t})} -> t
| {msg = Some({text = None})} -> ""
| {msg = None} -> ""

How can I share some values among different models in elm

Update 2016-05-14
I'm sorry that I may have failed to explain my problem clearly because of my bad English. This time I've abstracted the question and demonstrated it in a counter example.
There are two sub-modules(CounterPair and CounterTriplet), and each of them uses Counter to display num filed of SharedModel and displays something else (other Counters in this example). I highlighted the Counter displaying SharedModel's num with red color.
The red Counter of CounterPair and CounterTriple should synchronized because they're representing same SharedModel. Also, shared field of Model also need to be updated when any of the red Counters updates. The code shown below have only managed to display the things as I want but the update chain is missing. How should I implement the update chain ?
You can try this code on Try Elm.
import Html exposing (div, button, text)
import Html.App as App exposing (beginnerProgram)
import Html.Events exposing (onClick)
import Html.Attributes exposing (style)
main =
beginnerProgram { model = init, view = view, update = update }
-------------------------------------
type alias SharedModel =
{ num : Int
, foo : String
}
sharedInit =
SharedModel 0 "Foo"
--------------------------------------
type alias Model =
{ shared : SharedModel
, pair : CounterPair
, triplet : CounterTriplet
}
init =
let
shared = sharedInit
in
Model shared (pairInit shared) (tripletInit shared)
view model =
div []
[ App.map Pair (pairView model.pair)
, App.map Triplet (tripletView model.triplet)
]
type Msg
= Pair PairMsg
| Triplet TripletMsg
update msg model =
case msg of
Pair sub ->
let
pair = pairUpdate sub model.pair
in
{ model | pair = pair }
Triplet sub ->
let
triplet = tripletUpdate sub model.triplet
in
{ model | triplet = triplet }
----------------------------------------
type alias CounterTriplet =
{ localFst : CounterModel
, localSnd : CounterModel
, global : CounterModel
}
tripletInit shared =
CounterTriplet (counterInit 0) (counterInit 0) (counterInit shared.num)
tripletView model =
div [ style [("background-color","lightgray"), ("margin-bottom", "1rem")] ]
[ App.map TriLocalSnd (counterView "green" model.localFst)
, App.map TriLocalSnd (counterView "green" model.localSnd)
, App.map TriGlobal (counterView "red" model.global)
]
type TripletMsg
= TriLocalFst CounterMsg
| TriLocalSnd CounterMsg
| TriGlobal CounterMsg
tripletUpdate msg model =
case msg of
TriLocalFst sub ->
let
localFst = counterUpdate sub model.localFst
in
{ model | localFst = localFst }
TriLocalSnd sub ->
let
localSnd = counterUpdate sub model.localSnd
in
{ model | localSnd = localSnd }
TriGlobal sub ->
let
global = counterUpdate sub model.global
in
{ model | global = global }
----------------------------------------------
type alias CounterPair =
{ local : CounterModel
, global : CounterModel
}
pairInit shared =
CounterPair (counterInit 0) (counterInit shared.num)
pairView model =
div [ style [("background-color","lightgray"), ("margin-bottom", "1rem")] ]
[ App.map PairLocal (counterView "green" model.local)
, App.map PairGlobal (counterView "red" model.global)
]
type PairMsg
= PairLocal CounterMsg
| PairGlobal CounterMsg
pairUpdate msg model =
case msg of
PairLocal sub ->
let
local = counterUpdate sub model.local
in
{ model | local = local }
PairGlobal sub ->
let
global = counterUpdate sub model.global
in
{ model | global = global }
---------------------------------------
type alias CounterModel =
{ num : Int
, btnClicks : Int
}
counterInit num =
CounterModel num 0
counterView color model =
div [ style [("display","inline-block"), ("margin-right", "1rem")] ]
[ button [ onClick Decrement ] [ text "-" ]
, div [ style [("color", color)]] [ text (toString model.num) ]
, button [ onClick Increment ] [ text "+" ]
, div [ ] [ text ("btn click: " ++ (toString model.btnClicks)) ]
]
type CounterMsg = Increment | Decrement
counterUpdate msg model =
case msg of
Increment ->
{ model | num = model.num + 1, btnClicks = model.btnClicks + 1 }
Decrement ->
{ model | num = model.num - 1, btnClicks = model.btnClicks + 1 }
Original Question
I'm using The Elm Architecture and the view hierarchy is like
,--------------------------------------------------------------------.
| View |
| ,------------------. ,------------------------------------------. |
| | SubView1 | | SubView2 | |
| | ,--------------. | | ,---------------. ,--------------------. | |
| | | Label(title) | | | | Label(title) | | Label(description) | | |
| | `--------------' | | `---------------' `--------------------' | |
| `------------------' `------------------------------------------' |
`--------------------------------------------------------------------'
Label is a customized <label> which can turn into <input> field when double clicked. So it is editable.
The Models is like below. The problem is I want the content of ViewModel is updated when the sub-sub label's value is edited. When I update the title label of subview1, that of subview2 should also be updated because they should "share" same Content
But the value of LabelModel is somehow stand-alone so I need to transfer the change of value all way up to ViewModel. I don't think this is a practical way of implementing what I want, because the view hierarchy may become more complicated. Besides, almost all Models have something specific (Foo, Bar, BarBar, etc. used to store states), so they need to be initialized. So Making ViewModel neat, for example type alias ViewModel = Content, is impractical, because I don't know where to get Bar and BarBar for SubViewModel1 and SubViewModel2
type alias ViewModel =
{ content : Content
, subView1 : SubViewModel1
, subView2 : SubViewModel2
}
type alias SubViewModel1 =
{ content : Content
, label : Label
, bar : Bar
}
type alias SubViewModel2 =
{ content : Content
, titleLabel : Label
, descLabel : Label
, barbar : BarBar
}
type alias Content =
{ title: String
, description : String
}
type alias LabelModle =
{ value : String
, state : Foo
}
I'm very new to Elm so this question may seems stupid, sorry for that.
Thanks for reading (and answering :) )
Elm component hierarchies work best when a single piece of data is represented only in one place. If you need the data in more than one component, you place it at the nearest common ancestor of those components.
In your concrete case, if SubViewModel1.label.value and SubViewModel2.titleLabel.value is really the same piece of data, it should live at ViewModel.
Once the data lives in that one place, you react to updates in it by reacting in the update function of the ViewModel component to a Msg of SubViewModel1 or ...2. Something like this:
module View
import SubView1
import SubView2
type alias ViewModel =
{ content : Content
, subView1 : SubView1.Model
, subView2 : SubView2.Model
, label : String -- New!
}
type Msg
= SubView1Msg SubView1.Msg
| SubView2Msg SubView2.Msg
| ...
update action model =
case action of
...
(SubView1.UpdateLabel str) as action' ->
let
(subView1', fx) =
SubView1.update action' model.subView1
in
( { model
| subView1 = subView1'
, label = str
}
, Cmd.map SubView1Action fx
)

Concurrent Download with limited number of Workers and AsyncSeq from FSharpX (or ExtCore)

I try a concurrent download with limited number of Workers using the AsyncSeq module.
Based on the FSharpX example of https://github.com/fsprojects/fsharpx/blob/master/samples/Crawler.fsx
let rec concurrentDownload concurrentWorkers download transform (urls:string list) =
asyncSeq {
let requests = BlockingQueueAgent<_>(1000)
let results = BlockingQueueAgent<_>(50)
let worker() = async {
while true do
let! url = requests.AsyncGet()
let! doc = download url
match doc with
| Some txt -> do! results.AsyncAdd( transform txt )
| _ -> ()
}
// fill in all the requests
for url in urls do
do! requests.AsyncAdd url
// create the workers and start them
for i in 1 .. concurrentWorkers do
worker() |> Async.Start
// get the results and yield them in the asynchronous sequence
while requests.Count > 0 && results.Count > 0 do
let! res = results.AsyncGet()
yield res
}
let rand = new System.Random()
let rnd() = rand.Next(0,4000)
// a simulated download, sleep time depends on random number
let download str = async {
let name = "dl " + str
let payload = rnd()
printfn "Started : %s (payload=%d)" name payload
do! Async.Sleep(1000 + payload)
printfn "Finished: %s" name
return Some name
}
let urls = [1..10] |> List.map (sprintf "URL %d")
let concurrentWorkers = 5
let transform = id
let ret = concurrentDownload concurrentWorkers download transform urls
//ret // val it : AsyncSeq<string> = Microsoft.FSharp.Control.FSharpAsync`1[FSI_0012.FSharp.Control.AsyncSeqInner`1[System.String]]
let z =
ret
|> AsyncSeq.toBlockingSeq
|> Seq.toList
I assumed that z gets something like seq ["dl URL 3"; "dl URL 5"; ... ]
because 'download' returns Some content.
The workers on the blocking queues working as expected:
Started : dl URL 1 (payload=2281)
Started : dl URL 3 (payload=741)
Started : dl URL 4 (payload=3283)
Started : dl URL 5 (payload=1117)
Started : dl URL 2 (payload=2435)
Finished: dl URL 3
Started : dl URL 6 (payload=263)
Finished: dl URL 5
Started : dl URL 7 (payload=1115)
Finished: dl URL 6
Started : dl URL 8 (payload=1041)
Finished: dl URL 1
Started : dl URL 9 (payload=959)
Finished: dl URL 2
Started : dl URL 10 (payload=604)
Finished: dl URL 7
Finished: dl URL 4
Finished: dl URL 10
Finished: dl URL 8
Finished: dl URL 9
The problem is, why is z an empty list?
And not as exprected seq ["dl URL 3"; "dl URL 5"; ... ]?
As a reference, here is the toBlockingSeq function:
// FSharpX AsyncSeq.toBlockingSeq
let toBlockingSeq (input : AsyncSeq<'T>) =
// Write all elements to a blocking buffer and then add None to denote end
let buf = new BlockingQueueAgent<_>(1)
async {
do! iterAsync (Some >> buf.AsyncAdd) input
do! buf.AsyncAdd None
} |> Async.Start
// Read elements from the blocking buffer & return a sequences
let rec loop () =
seq {
match buf.Get() with
| None -> ()
| Some v ->
yield v
yield! loop()
}
loop ()

Resources