The RSS reader tutorial (Step 2)

In the pre­vi­ous step, we have suc­cess­fully im­ple­mented the first end­point of the RSS reader app.

The RSS reader ex­am­ple as­sumes im­ple­ment­ing 3 end­points. This ar­ti­cle is ded­i­cated to im­ple­ment­ing the GET /user/{user_id}/rss_channels end­point.

Be­fore com­plet­ing this step, make sure your are in the step_2 git branch:

$ git checkout step_2

Implementing the second endpoint

The sec­ond end­point pro­duces an array of RSS chan­nels by given user_id.

We need to ex­e­cute the two fol­low­ing 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 chan­nel de­tails for a given link:
    SELECT description, title, site_link, rss_link FROM channel_info_by_rss_link WHERE rss_link = GIVEN_LINK ;
    

Implementation

The end­point al­lows the the front-​end app to dis­play the list of RSS feeds a user sub­scribed on. When the end­point is ac­cessed, the AppVerticle#getRssChannels method is called. We can im­ple­ment 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 ini­tial­ized 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 suc­cess­fully im­ple­mented the sec­ond end­point, which al­lows the browser app to ob­tain chan­nels in­for­ma­tion for a spe­cific user. To en­sure that it is work­ing fine, point your browser to localhost:8080 and click to the re­fresh but­ton. Chan­nel list should ap­pear im­me­di­ately.

If you have any prob­lems with com­plet­ing this step you can check­out to step_3, where you can find all changes made for com­plet­ing this step:

$ git checkout step_3

Thanks for read­ing this. I hope you en­joyed read­ing this ar­ti­cle. See you soon on our Git­ter chan­nel!

Next post

Eclipse Vert.x 3.5.4

We have just released Vert.x 3.5.4, a bug fix release of Vert.x 3.5.x.

Read more
Previous post

The RSS reader tutorial

In this tutorial, you will learn how to use the Eclipse Vert.x Cassandra client in practice. We will develop an RSS reader with three HTTP endpoints.

Read more
Related posts

The RSS reader tutorial

In this tutorial, you will learn how to use the Eclipse Vert.x Cassandra client in practice. We will develop an RSS reader with three HTTP endpoints.

Read more

Easy SSO for Vert.x with Keycloak

In this blog post, you'll learn how to implement Single Sign-on with OpenID Connect and how to use Keycloak together with Eclipse Vert.x.

Read more

The RSS reader tutorial (Step 3)

This is the third installment of our Vert.x Cassandra Client tutorial. We will implement the last RSS endpoint serving a list of articles related to a specific channel.

Read more