forEach & Promise.all() doesn't works - adonis.js

I have a problem with forEach Promise... It doesn't works on AdonisJS.
await Promise.all(items.map(async (item) => {
console.log(item)
}))
No console logged...

I would bet that what the Promise.all return is an empty list, otherwise the console would have logged at least something ^^
Ensure the items object is an iterable object so that Promise.all will not throw an exception
Let's say we test this using adonis cli repl inside your adonis project root so that you can test your application models and also to be sure that your issue does not come from adonis
#!/bin/bash
cd <your adonis project root folder>
adonis repl
# stupid test with a fake iterable object
const MAX_ITEMS = 5
const items = []
for (let i = 0; i < MAX_ITEMS; ++i) {
items.push(i % 2 === 0? i / 2: i * 3 + 1)
}
// let items = []
await Promise.all(items.map(async (item) => {
console.log(item)
return item % 2 === 0 ? item / 2: item * 3 + 1
}))
Now the console would have logged this
0
4
1
10
2
And the result of the Promise.all call would have given this
[ 0, 2, 4, 5, 1 ]
If you use Promise.all without an iterable object you would see some errors (let you test in using adonis repl)
adonis repl
let itemsAsObject = {}
let itemsAsUndefined = undefined
let itemsAsEmptyList = []
async function testPromise(items) {
return Promise.all(items.map(async (item) => {
console.log(item)
return await item % 2 === 0 ? item / 2: item * 3 + 1
}))
}
await testPromise(itemsAsObject) // throws an error
await testPromise(itemsAsUndefined) // throws an error
await testPromise(itemsAsEmptyList) // returns []
You could use the same mechanic with your own Lucid Models
Cheers :)

Related

LINQ query throw an exception when to get a count of a type

I write a test to filter a list.
Service.cs
public async Task<GetAllParticipantsDto> ListAllAsync(GetAllParticipantsRequest queryModel)
{
int count = 0;
var participantsList = participantRepository.AsNoTracking()
.Include(a => a.ParticipantConnections)!.ThenInclude(a => a.Connection)
.Include(a => a.ParticipantApplicationUsers)!.ThenInclude(a => a.ApplicationUser)
.AsQueryable();
if (participantsList != null && !String.IsNullOrEmpty(queryModel.RelatedConnection))
{
participantsList = participantsList.Where(x => x.ParticipantConnections.Any(y => y.Connection.FirstName.ToLower().Contains(queryModel.RelatedConnection.ToLower())) || x.ParticipantConnections.Any(y => y.Connection.LastName.ToLower().Contains(queryModel.RelatedConnection.ToLower())) ||
x.ParticipantConnections.Any(y => (y.Connection.FirstName.ToLower()+ y.Connection.LastName.ToLower()).Contains(queryModel.RelatedConnection.ToLower())) || x.ParticipantConnections.Any(y => (y.Connection.FirstName.ToLower() +" "+ y.Connection.LastName.ToLower()).Contains(queryModel.RelatedConnection.ToLower())));
count = participantsList.Count(); // NullReferenceException Exception thrown in this line
}
}
unit test
public async void ListAllAsync_ShouldReturn_FilterByBy_RelatedConnection()
{
var query = new GetAllParticipantsRequest()
{ // others null
RelatedConnection = "Alice Doe, Bob Doe"
};
Func<DSMP.ApplicationCore.Entities.Participant, bool> exists = n => true; // prepare Func outside of Setup
//1 - create a List<T> with test items
var participantList = ParticipantMockData.ListAllAsyncEntity();
//2 - build mock by extension
var mock = participantList.AsQueryable().BuildMock();
//3 - setup the mock as Queryable for Moq
_participantRepository.Setup(x => x.AsNoTracking(false)).Returns(mock);
var sut = new ParticipantService(
_participantRepository.Object, _participantSupportNeed.Object, _participantConnection.Object, _participantDisability.Object, _participantMedicalCondition.Object, _supportNeed.Object, _disability.Object, _medicalCondition.Object, _participantApplicationUser.Object, _appLogger.Object);
// Act
var result = await sut.ListAllAsync(query);
// Assert
}
The exception
Message - System.NullReferenceException: 'Object reference not set to an instance of an object.'
StackTrace -
" at System.Linq.Enumerable.Any[TSource](IEnumerable1 source, Func2 predicate)\r\n at System.Linq.Enumerable.WhereListIterator1.MoveNext()\r\n at System.Collections.Generic.LargeArrayBuilder1.AddRange(IEnumerable1 items)\r\n at System.Collections.Generic.EnumerableHelpers.ToArray[T](IEnumerable1 source)\r\n at System.Linq.Enumerable.ToArray[TSource](IEnumerable1 source)\r\n at System.Linq.SystemCore_EnumerableDebugView1.get_Items()"
I could not find why this error occur.
Can any one help me?

I'm not able to test my sails.js controller with chai and sinon

I have a controller Acounts with a method sum, and a service file named validation with an object register. This object has a method validate that validates the form provided and returns a Boolean.
Controller
sum: function(req, res) {
//validate form with a validation service function
const validator = new validation.register();
let check = validator.validate(req.body.form);
let count = 0;
if (check) {
count += 1;
} else {
count -= 1;
}
res.send(count);
},
test
//imports
const chai = require("chai");
const expect = chai.expect;
const sinon = require("sinon");
const util = require('util'); // to print complex objects
const acountsC = require("../../../api/controllers/AcountsController.js");
describe("AcountsController", function() {
describe("sum", function() {
let req = {
body: {
form: {}
}
}
let res = {
send: sinon.spy()
}
let validation = {
register: {
validate: function() {}
}
}
let stub_validate = sinon.stub(validation.register, "validate").returns(true);
it("count should be 1 when validation is true", function() {
acountsC.sum(req, res);
expect(count).to.equal(1);
});
});
});
test log
AcountsController
sum
1) count should be 1 when validation is true
0 passing (5s)
1 failing
1) AcountsController
sum
count should be 1 when validation is true:
ReferenceError: count is not defined
note
I understand that the test is supposed to execute the piece of code we are calling, while replacing the external functions called in that piece of code(The controller) making it return whatever we set. If the test is executing that piece of code, why I can't access any variables created in the controller?
I've tried by spying res.send(), and check if it was called with a 1. I didn't succeed.
I searched everywhere how to perform an assertion to a variable, but I found nothing. :(
hope you can help
Here is the unit test solution:
accountController.js:
const validation = require('./validation');
class AccountController {
sum(req, res) {
const validator = new validation.register();
const check = validator.validate(req.body.form);
let count = 0;
if (check) {
count += 1;
} else {
count -= 1;
}
res.send(count);
}
}
module.exports = AccountController;
validation.js:
class Register {
validate() {}
}
module.exports = {
register: Register,
};
accountController.test.js:
const AccountController = require('./accountController');
const sinon = require('sinon');
const validation = require('./validation');
describe('60182912', () => {
afterEach(() => {
sinon.restore();
});
describe('#sum', () => {
it('should increase count and send', () => {
const registerInstanceStub = {
validate: sinon.stub().returns(true),
};
const registerStub = sinon.stub(validation, 'register').callsFake(() => registerInstanceStub);
const accountController = new AccountController();
const mRes = { send: sinon.stub() };
const mReq = { body: { form: {} } };
accountController.sum(mReq, mRes);
sinon.assert.calledWithExactly(mRes.send, 1);
sinon.assert.calledOnce(registerStub);
sinon.assert.calledWithExactly(registerInstanceStub.validate, {});
});
it('should decrease count and send', () => {
const registerInstanceStub = {
validate: sinon.stub().returns(false),
};
const registerStub = sinon.stub(validation, 'register').callsFake(() => registerInstanceStub);
const accountController = new AccountController();
const mRes = { send: sinon.stub() };
const mReq = { body: { form: {} } };
accountController.sum(mReq, mRes);
sinon.assert.calledWithExactly(mRes.send, -1);
sinon.assert.calledOnce(registerStub);
sinon.assert.calledWithExactly(registerInstanceStub.validate, {});
});
});
});
Unit test results with coverage report:
60182912
#sum
✓ should increase count and send
✓ should decrease count and send
2 passing (10ms)
----------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------------------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 50 | 100 |
accountController.js | 100 | 100 | 100 | 100 |
validation.js | 100 | 100 | 0 | 100 |
----------------------|---------|----------|---------|---------|-------------------
source code: https://github.com/mrdulin/expressjs-research/tree/master/src/stackoverflow/60182912
The problem was the lifecycle file I created trusting in sails docs. That documentation is for integrated testing due to the fact that it lifts sails before any other test. This is quite slow while unit tests should be fast. Erasing that file was enough to test the controller successfully. Otherwise sails messes up with the tests in a way I don't even fully understand. I suppose it is due to sails making services globally available. So when my controller calls for validation service, this one returns some default and not what the stub says it should return.
UPDATE:
I managed to make it work. When lifting sails before testing, only the tested controller should be require, services and models shouldn't.

Why is my jest async action creator test not working?

I am very new to unit testing, and am trying to go through my react-redux project to write some tests.
Why is this test not working, and how could I make it pass?
Here is the test. I want to test my fetch posts action creator. This is for a small blog application.:
import configureStore from 'redux-mock-store'; // ES6 modules
import { findSinglePost, sendEdit, changeRedirect, checkBoxChange } from '../client/redux/actions/postActions';
import thunk from 'redux-thunk';
import axios from 'axios';
const middlewares = [thunk];
const mockStore = configureStore(middlewares);
describe('asynchronous action creators', () => {
it('should fetch posts', () => {
let store = mockStore({})
//my async action creator. It uses mock data that's in the same folder.
const fetchPosts = () => function(dispatch) {
dispatch({type: 'FETCH_POSTS'});
return axios.get('./MOCK.json').then((response) => {
dispatch({type: 'FETCH_POSTS_FUFILLED', payload: response.data});
}).catch((err) => {
dispatch({type: 'FETCH_POSTS_REJECTED', payload: err});
});
};
//this doesn't equal FETCH_POSTS_FUFILLED, it ends up equaling just "FETCH_POSTS"
return store.dispatch(fetchPosts()).then(() => {
const actions = store.getActions();
expect(actions[0]).toEqual({type: 'FETCH_POSTS_FUFILLED'});
})
})
});
Here is jest's feedback. I want it to equal 'FETCH_POSTS_'FUFILLED', but it's returning 'FETCH_POSTS'. :
FAIL _test_\actions.test.js
● asynchronous action creators › should fetch posts
expect(received).toEqual(expected)
Expected value to equal:
{"type": "FETCH_POSTS_FUFILLED"}
Received:
{"type": "FETCH_POSTS"}
Difference:
- Expected
+ Received
Object {
- "type": "FETCH_POSTS_FUFILLED",
+ "type": "FETCH_POSTS",
}
88 | return store.dispatch(fetchPosts()).then(() => {
89 | const actions = store.getActions();
> 90 | expect(actions[0]).toEqual({type: 'FETCH_POSTS_FUFILLED'});
91 | })
92 | })
93 | });
at _test_/actions.test.js:90:26
PASS client\views\LoginPage\LoginPage.test.jsx
Test Suites: 1 failed, 1 passed, 2 total
Tests: 1 failed, 5 passed, 6 total
Snapshots: 0 total
Time: 1.49s
Ran all test suites related to changed files.
Also, here is the project's github repo if you want to try to run it.
Also, if there's a standard way in the industry that's more well known on how to do this, I'd love the advice.
Edit:
When I change actions[0] to actions[ 1] I get this error:
Expected value to equal:
{"type": "FETCH_POSTS_FUFILLED"}
Received:
{"payload": {Symbol(impl): {"message": "The string did not match the expected pattern.", "name": "SyntaxError", Symbol(wrapper): [Circular]}}, "type": "FETCH_POSTS_REJECTED"}
Difference:
- Expected
+ Received
Object {
- "type": "FETCH_POSTS_FUFILLED",
+ "payload": DOMException {
+ Symbol(impl): DOMExceptionImpl {
+ "message": "The string did not match the expected pattern.",
+ "name": "SyntaxError",
+ Symbol(wrapper): [Circular],
+ },
+ },
+ "type": "FETCH_POSTS_REJECTED",
Here is the picture form of jest's feedback:
The mocked store you are using will store all dispatched calls that have been made to it. In your case, two dispatch calls should be made, the first being FETCH_POSTS and the second being either FETCH_POST_FULFILLED or FETCH_POST_REJECTED.
Hence when you retrieve the dispatched actions from the mocked store, the first entry (which you are using in your expect) will be the FETCH_POSTS. You should check the second value in the array, which would be either FETCH_POSTS_FULFILLED or FETCH_POSTS_REJECTED based on how the promise is resolved in the function you are testing.

Is it possible to create an "infinite" stream from a database table using Akka Stream

I'm playing with Akka Streams 2.4.2 and am wondering if it's possible to setup a stream which uses a database table for a source and whenever there is a record added to the table that record is materialized and pushed downstream?
UPDATE: 2/23/16
I've implemented the solution from #PH88. Here's my table definition:
case class Record(id: Int, value: String)
class Records(tag: Tag) extends Table[Record](tag, "my_stream") {
def id = column[Int]("id")
def value = column[String]("value")
def * = (id, value) <> (Record.tupled, Record.unapply)
}
Here's the implementation:
implicit val system = ActorSystem("Publisher")
implicit val materializer = ActorMaterializer()
val db = Database.forConfig("pg-postgres")
try{
val newRecStream = Source.unfold((0, List[Record]())) { n =>
try {
val q = for (r <- TableQuery[Records].filter(row => row.id > n._1)) yield (r)
val r = Source.fromPublisher(db.stream(q.result)).collect {
case rec => println(s"${rec.id}, ${rec.value}"); rec
}.runFold((n._1, List[Record]())) {
case ((id, xs), current) => (current.id, current :: xs)
}
val answer: (Int, List[Record]) = Await.result(r, 5.seconds)
Option(answer, None)
}
catch { case e:Exception => println(e); Option(n, e) }
}
Await.ready(newRecStream.throttle(1, 1.second, 1, ThrottleMode.shaping).runForeach(_ => ()), Duration.Inf)
}
finally {
system.shutdown
db.close
}
But my problem is that when I attempt to call flatMapConcat the type I get is Serializable.
UPDATE: 2/24/16
Updated to try db.run suggestion from #PH88:
implicit val system = ActorSystem("Publisher")
implicit val materializer = ActorMaterializer()
val db = Database.forConfig("pg-postgres")
val disableAutoCommit = SimpleDBIO(_.connection.setAutoCommit(false))
val queryLimit = 1
try {
val newRecStream = Source.unfoldAsync(0) { n =>
val q = TableQuery[Records].filter(row => row.id > n).take(queryLimit)
db.run(q.result).map { recs =>
Some(recs.last.id, recs)
}
}
.throttle(1, 1.second, 1, ThrottleMode.shaping)
.flatMapConcat { recs =>
Source.fromIterator(() => recs.iterator)
}
.runForeach { rec =>
println(s"${rec.id}, ${rec.value}")
}
Await.ready(newRecStream, Duration.Inf)
}
catch
{
case ex: Throwable => println(ex)
}
finally {
system.shutdown
db.close
}
Which works (I changed query limit to 1 since I only have a couple items in my database table currently) - except once it prints the last row in the table the program exists. Here's my log output:
17:09:27,982 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
17:09:27,982 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]
17:09:27,982 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback.xml] at [file:/Users/xxxxxxx/dev/src/scratch/scala/fpp-in-scala/target/scala-2.11/classes/logback.xml]
17:09:28,062 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]
17:09:28,064 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [STDOUT]
17:09:28,079 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property
17:09:28,102 |-INFO in ch.qos.logback.classic.joran.action.LoggerAction - Setting level of logger [application] to DEBUG
17:09:28,103 |-INFO in ch.qos.logback.classic.joran.action.RootLoggerAction - Setting level of ROOT logger to INFO
17:09:28,103 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [STDOUT] to Logger[ROOT]
17:09:28,103 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - End of configuration.
17:09:28,104 |-INFO in ch.qos.logback.classic.joran.JoranConfigurator#4278284b - Registering current configuration as safe fallback point
17:09:28.117 [main] INFO com.zaxxer.hikari.HikariDataSource - pg-postgres - is starting.
1, WASSSAAAAAAAP!
2, WHAAAAT?!?
3, booyah!
4, what!
5, This rocks!
6, Again!
7, Again!2
8, I love this!
9, Akka Streams rock
10, Tuning jdbc
17:09:39.000 [main] INFO com.zaxxer.hikari.pool.HikariPool - pg-postgres - is closing down.
Process finished with exit code 0
Found the missing piece - need to replace this:
Some(recs.last.id, recs)
with this:
val lastId = if(recs.isEmpty) n else recs.last.id
Some(lastId, recs)
The call to recs.last.id was throwing java.lang.UnsupportedOperationException: empty.last when the result set was empty.
In general SQL database is a 'passive' construct and does not actively push changes like what you described. You can only 'simulate' the 'push' with periodic polling like:
val newRecStream = Source
// Query for table changes
.unfold(initState) { lastState =>
// query for new data since lastState and save the current state into newState...
Some((newState, newRecords))
}
// Throttle to limit the poll frequency
.throttle(...)
// breaks down into individual records...
.flatMapConcat { newRecords =>
Source.unfold(newRecords) { pendingRecords =>
if (records is empty) {
None
} else {
// take one record from pendingRecords and save to newRec. Save the rest into remainingRecords.
Some(remainingRecords, newRec)
}
}
}
Updated: 2/24/2016
Pseudo code example based on the 2/23/2016 updates of the question:
implicit val system = ActorSystem("Publisher")
implicit val materializer = ActorMaterializer()
val db = Database.forConfig("pg-postgres")
val queryLimit = 10
try {
val completion = Source
.unfoldAsync(0) { lastRowId =>
val q = TableQuery[Records].filter(row => row.id > lastRowId).take(queryLimit)
db.run(q.result).map { recs =>
Some(recs.last.id, recs)
}
}
.throttle(1, 1.second, 1, ThrottleMode.shaping)
.flatMapConcat { recs =>
Source.fromIterator(() => recs.iterator)
}
.runForeach { rec =>
println(s"${rec.id}, ${rec.value}")
}
// Block forever
Await.ready(completion, Duration.Inf)
} catch {
case ex: Throwable => println(ex)
} finally {
system.shutdown
db.close
}
It will repeatedly execute the query in unfoldAsync against the DB, retrieving at most 10 (queryLimit) records a time and send the records downstream (-> throttle -> flatMapConcat -> runForeach). The Await at the end will actually block forever.
Updated: 2/25/2016
Executable 'proof-of-concept' code:
import akka.actor.ActorSystem
import akka.stream.{ThrottleMode, ActorMaterializer}
import akka.stream.scaladsl.Source
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, Future}
import scala.concurrent.duration._
object Infinite extends App{
implicit val system = ActorSystem("Publisher")
implicit val ec = system.dispatcher
implicit val materializer = ActorMaterializer()
case class Record(id: Int, value: String)
try {
val completion = Source
.unfoldAsync(0) { lastRowId =>
Future {
val recs = (lastRowId to lastRowId + 10).map(i => Record(i, s"rec#$i"))
Some(recs.last.id, recs)
}
}
.throttle(1, 1.second, 1, ThrottleMode.Shaping)
.flatMapConcat { recs =>
Source.fromIterator(() => recs.iterator)
}
.runForeach { rec =>
println(rec)
}
Await.ready(completion, Duration.Inf)
} catch {
case ex: Throwable => println(ex)
} finally {
system.shutdown
}
}
Here is database infinite streaming working code. This has been tested with millions of records being inserted into postgresql database while streaming app is running -
package infinite.streams.db
import akka.NotUsed
import akka.actor.ActorSystem
import akka.stream.alpakka.slick.scaladsl.SlickSession
import akka.stream.scaladsl.{Flow, Sink, Source}
import akka.stream.{ActorMaterializer, ThrottleMode}
import org.slf4j.LoggerFactory
import slick.basic.DatabaseConfig
import slick.jdbc.JdbcProfile
import scala.concurrent.duration._
import scala.concurrent.{Await, ExecutionContextExecutor}
case class Record(id: Int, value: String) {
val content = s"<ROW><ID>$id</ID><VALUE>$value</VALUE></ROW>"
}
object InfiniteStreamingApp extends App {
println("Starting app...")
implicit val system: ActorSystem = ActorSystem("Publisher")
implicit val ec: ExecutionContextExecutor = system.dispatcher
implicit val materializer: ActorMaterializer = ActorMaterializer()
println("Initializing database configuration...")
val databaseConfig: DatabaseConfig[JdbcProfile] = DatabaseConfig.forConfig[JdbcProfile]("postgres3")
implicit val session: SlickSession = SlickSession.forConfig(databaseConfig)
import databaseConfig.profile.api._
class Records(tag: Tag) extends Table[Record](tag, "test2") {
def id = column[Int]("c1")
def value = column[String]("c2")
def * = (id, value) <> (Record.tupled, Record.unapply)
}
val db = databaseConfig.db
println("Prime for streaming...")
val logic: Flow[(Int, String), (Int, String), NotUsed] = Flow[(Int, String)].map {
case (id, value) => (id, value.toUpperCase)
}
val fetchSize = 5
try {
val done = Source
.unfoldAsync(0) {
lastId =>
println(s"Fetching next: $fetchSize records with id > $lastId")
val query = TableQuery[Records].filter(_.id > lastId).take(fetchSize)
db.run(query.result.withPinnedSession)
.map {
recs => Some(recs.last.id, recs)
}
}
.throttle(5, 1.second, 1, ThrottleMode.shaping)
.flatMapConcat {
recs => Source.fromIterator(() => recs.iterator)
}
.map(x => (x.id, x.content))
.via(logic)
.log("*******Post Transformation******")
// .runWith(Sink.foreach(r => println("SINK: " + r._2)))
// Use runForeach or runWith(Sink)
.runForeach(rec => println("REC: " + rec))
println("Waiting for result....")
Await.ready(done, Duration.Inf)
} catch {
case ex: Throwable => println(ex.getMessage)
} finally {
println("Streaming end successfully")
db.close()
system.terminate()
}
}
application.conf
akka {
loggers = ["akka.event.slf4j.Slf4jLogger"]
loglevel = "INFO"
}
# Load using SlickSession.forConfig("slick-postgres")
postgres3 {
profile = "slick.jdbc.PostgresProfile$"
db {
dataSourceClass = "slick.jdbc.DriverDataSource"
properties = {
driver = "org.postgresql.Driver"
url = "jdbc:postgresql://localhost/testdb"
user = "postgres"
password = "postgres"
}
numThreads = 2
}
}

Calling arbitrary number of WS.url().get() in sequence

I have a List[String] of URLs that I want to load and process (parse, store to database) in sequence.
I found only fixed-length examples, like:
def readUrls = Action {
implicit request => {
implicit val context = scala.concurrent.ExecutionContext.Implicits.global
val url1 = "http://some-website.com"
val url2 = "http://other-website.com"
Async {
for {
result1 <- WS.url(url1).get()
result2 <- WS.url(url2).get()
} yield {
Ok(result1.body + result2.body)
}
}
}
But instead of url1 and url2, I need to process this puppy:
val urls = List("http://some-website.com", "http://other-website.com")
Thanks a bunch for any tips and advice!
If you want to chain Futures together arbitrarily in sequence, foldLeft ought to do the job:
urls.foldLeft(Future.successful[String]("")){ case (left, nextUrl) =>
left.flatMap{ aggregatedResult =>
WS.url(nextUrl).get().map( newResult =>
aggregatedResult + newResult.body
)
}
}
Since you're just combining the request bodies together, I gave the foldLeft an initial value of a Future empty String, which each step in the fold will then add on the next response body.
def executeUrls(urls: List[String]): Future[String] = {
urls.foldLeft(Future(""))((accumulator, url) => {
accumulator.flatMap(acc => {
WS.url(url).get().map(response => {
acc + response.body
})
}
})
}
This should be what you're looking for, note that it returns a new Future.
Edit: apparently LimbSoup was faster.