The RSS reader tutorial (Step 2)

In the previous step, we have successfully implemented the first endpoint of the RSS reader app.

The RSS reader example assumes implementing 3 endpoints. This article is dedicated to implementing the GET /user/{user_id}/rss_channels endpoint.

Before completing this step, make sure your are in the step_2 git branch:

$ git checkout step_2

Implementing the second endpoint

The second endpoint produces an array of RSS channels by given user_id.

We need to execute the two following queries to:

  1. Fetch RSS links for a given user:
    SELECT rss_link FROM rss_by_user WHERE login = GIVEN_USER_ID ;
  2. Fetch RSS channel details for a given link:
    SELECT description, title, site_link, rss_link FROM channel_info_by_rss_link WHERE rss_link = GIVEN_LINK ;

Implementation

The endpoint allows the the front-end app to display the list of RSS feeds a user subscribed on. When the endpoint is accessed, the AppVerticle#getRssChannels method is called. We can implement this method in this way:

private void getRssChannels(RoutingContext ctx) {
    String userId = ctx.request().getParam("user_id");
    if (userId == null) {
        responseWithInvalidRequest(ctx);
    } else {
        Future<List<Row>> future = Future.future();
        client.executeWithFullFetch(selectRssLinksByLogin.bind(userId), future);
        future.compose(rows -> {
            List<String> links = rows.stream()
                    .map(row -> row.getString(0))
                    .collect(Collectors.toList());
 
            return CompositeFuture.all(
                    links.stream().map(selectChannelInfo::bind).map(statement -> {
                        Future<List<Row>> channelInfoRow = Future.future();
                        client.executeWithFullFetch(statement, channelInfoRow);
                        return channelInfoRow;
                    }).collect(Collectors.toList())
            );
        }).setHandler(h -> {
            if (h.succeeded()) {
                CompositeFuture result = h.result();
                List<List<Row>> results = result.list();
                List<Row> list = results.stream()
                        .flatMap(List::stream)
                        .collect(Collectors.toList());
                JsonObject responseJson = new JsonObject();
                JsonArray channels = new JsonArray();
 
                list.forEach(eachRow -> channels.add(
                        new JsonObject()
                                .put("description", eachRow.getString(0))
                                .put("title", eachRow.getString(1))
                                .put("link", eachRow.getString(2))
                                .put("rss_link", eachRow.getString(3))
                ));
 
                responseJson.put("channels", channels);
                ctx.response().end(responseJson.toString());
            } else {
                log.error("failed to get rss channels", h.cause());
                ctx.response().setStatusCode(500).end("Unable to retrieve the info from C*");
            }
        });
    }
}

Also, this method uses selectChannelInfo and selectRssLinksByLogin fields, they should be initialized in the AppVerticle#prepareNecessaryQueries method:

private Future<Void> prepareNecessaryQueries() {
    Future<PreparedStatement> selectChannelInfoPrepFuture = Future.future();
    client.prepare("SELECT description, title, site_link, rss_link FROM channel_info_by_rss_link WHERE rss_link = ? ;", selectChannelInfoPrepFuture);
 
    Future<PreparedStatement> selectRssLinkByLoginPrepFuture = Future.future();
    client.prepare("SELECT rss_link FROM rss_by_user WHERE login = ? ;", selectRssLinkByLoginPrepFuture);
 
    Future<PreparedStatement> insertNewLinkForUserPrepFuture = Future.future();
    client.prepare("INSERT INTO rss_by_user (login , rss_link ) VALUES ( ?, ?);", insertNewLinkForUserPrepFuture);
 
    return CompositeFuture.all(
            selectChannelInfoPrepFuture.compose(preparedStatement -> {
                selectChannelInfo = preparedStatement;
                return Future.succeededFuture();
            }),
            selectRssLinkByLoginPrepFuture.compose(preparedStatement -> {
                selectRssLinksByLogin = preparedStatement;
                return Future.succeededFuture();
            }),
            insertNewLinkForUserPrepFuture.compose(preparedStatement -> {
                insertNewLinkForUser = preparedStatement;
                return Future.succeededFuture();
            })
    ).mapEmpty();
}

Conclusion

In this part, we have successfully implemented the second endpoint, which allows the browser app to obtain channels information for a specific user. To ensure that it is working fine, point your browser to localhost:8080 and click to the refresh button. Channel list should appear immediately.

If you have any problems with completing this step you can checkout to step_3, where you can find all changes made for completing this step:

$ git checkout step_3

Thanks for reading this. I hope you enjoyed reading this article. See you soon on our Gitter channel!

Posted on 5 September 2018
in guides
3 min read

Related posts