Objectives

Create a new standard java project specifically to test the API we have developed in donation-service-play project.

donation-service-test

Create a new Java Project in eclipse - this time neither a play nor an Android app, just a simple Java Application. Call the Project donation-service-play-test"

Now download this archive here:

Expand the archive and drag/drop the folder into your project. It should now look like this:

Select all of the imported jar files, right-click, and select 'Build Path->Add to Build Path'

The project should now be configured thus:

Model Classes

app.donation.model

In donation-service-test, create a new package called 'app.donation.model'. Incorporate the following two classes into this package:

User

package app.donation.model;

public class User
{
  public String  _id;
  public String firstName;
  public String lastName;
  public String email;
  public String password;

  public User(String firstName, String lastName, String email, String password)
  {
    this.firstName = firstName;
    this.lastName = lastName;
    this.email = email;
    this.password = password;
  }
}

Candidate

package app.donation.model;

public class Candidate
{
  public String   _id;
  public String firstName;
  public String lastName;
  public String office;

  public Candidate(String firstName, String lastName, String office)
  {
    this.firstName = firstName;
    this.lastName = lastName;
    this.office = office;
  }
}

Donation

package app.donation.model;

public class Donation
{
  public String   _id;
  public int    amount;
  public String method;

  public Donation (int amount, String method)
  {
    this.amount = amount;
    this.method = method;
  }
}

These are java versions of the Mongoose javascript models we have developed in donaiton-web. Compare them now - can you see any differences?

In particular, look closely at Donation.

API Classes

In donation-service-test, create a new package called 'app.donation.api'. Incorporate the following two classes into this package:

DonationService

package app.donation.api;

import java.util.List;
import app.donation.model.Candidate;
import app.donation.model.Donation;
import app.donation.model.User;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Path;

public interface DonationService
{
  @GET("/api/users")
  Call<List<User>> getAllUsers();

  @GET("/api/users/{id}")
  Call<User> getUser(@Path("id") String id);

  @POST("/api/users")
  Call<User> createUser(@Body User User);

  @GET("/api/donations")
  Call<List<Donation>> getAllDonations();

  @GET("/api/candidates")
  Call<List<Candidate>> getAllCandidates();

  @POST("/api/candidates/{id}/donations")
  Call<Donation> createDonation(@Path("id") String id, @Body Donation donation);
}

DonationServiceAPI

package app.donation.api;

import java.util.List;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import app.donation.model.Candidate;
import app.donation.model.User;
import app.donation.model.Donation;
import retrofit2.Call;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class DonationServiceAPI
{
  DonationService service;

  public DonationServiceAPI(String url)
  {
    String service_url  = url;

    Gson gson = new GsonBuilder().create();

    Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(service_url)
        .addConverterFactory(GsonConverterFactory.create(gson))
        .build();
    service = retrofit.create(DonationService.class);
  }

  public List<User> getUsers() throws Exception
  {
    Call<List<User>> call = (Call<List<User>>) service.getAllUsers();
    Response<List<User>> users = call.execute();
    return users.body();
  }

  public List<Candidate> getAllCandidates() throws Exception
  {
    Call<List<Candidate>> call = (Call<List<Candidate>>) service.getAllCandidates();
    Response<List<Candidate>> candidates = call.execute();
    return candidates.body();
  }

  public List<Donation> getAllDonations() throws Exception
  {
    Call<List<Donation>> call = (Call<List<Donation>>) service.getAllDonations();
    Response<List<Donation>> donations = call.execute();
    return donations.body();
  }

  public User createUser(User newUser) throws Exception
  {
    Call<User> call = (Call<User>) service.createUser(newUser);
    Response<User> returnedUser = call.execute();
    return returnedUser.body();
  }

  public Donation createDonation(Candidate candidate, Donation newDonation) throws Exception
  {

    Call<Donation> call = (Call<Donation>) service.createDonation(candidate._id, newDonation);
    Response<Donation> returnedDonation = call.execute();
    return returnedDonation.body();
  }
}

Main

Create another package called 'app.donation.test', and incorporate this class:

public class Main
{
  static Gson gson = new GsonBuilder().setPrettyPrinting().create();

  public static void println(Object o)
  {
    System.out.println(gson.toJson(o));
  }

  public static void println(String s)
  {
    System.out.println(s);
  }

  public static void main(String[] args) throws Exception
  {
    DonationServiceAPI service = new DonationServiceAPI("http://localhost:4000");
    List<Candidate> candidates = service.getAllCandidates();
    List<User> users = service.getUsers();
    List<Donation> donations = service.getAllDonations();

    println ("--------------------");
    println ("  all users: ");
    println ("--------------------");
    println(users);
    println ("--------------------");
    println ("  all candidates: ");
    println ("--------------------");
    println(candidates);
    println ("--------------------");
    println ("  all donations: ");
    println ("--------------------");
    println(donations);
  }
}

Make sure Mongod + donation-web are running, and execute this main program now. It should print the following to the console:

--------------------
  all users: 
--------------------
[
  {
    "_id": "58159f0d776b3c73f442f65a",
    "firstName": "Homer",
    "lastName": "Simpson",
    "email": "homer@simpson.com",
    "password": "secret"
  },
  {
    "_id": "58159f0d776b3c73f442f65b",
    "firstName": "Marge",
    "lastName": "Simpson",
    "email": "marge@simpson.com",
    "password": "secret"
  },
  {
    "_id": "58159f0d776b3c73f442f65c",
    "firstName": "Bart",
    "lastName": "Simpson",
    "email": "bart@simpson.com",
    "password": "secret"
  }
]
--------------------
  all candidates: 
--------------------
[
  {
    "_id": "58159f0d776b3c73f442f65d",
    "firstName": "Lisa",
    "lastName": "Simpson",
    "office": "President"
  },
  {
    "_id": "58159f0d776b3c73f442f65e",
    "firstName": "Donald",
    "lastName": "Simpson",
    "office": "President"
  }
]
--------------------
  all donations: 
--------------------
[
  {
    "_id": "58159f0d776b3c73f442f65f",
    "amount": 40,
    "method": "paypal"
  },
  {
    "_id": "58159f0d776b3c73f442f660",
    "amount": 90,
    "method": "direct"
  },
  {
    "_id": "58159f0d776b3c73f442f661",
    "amount": 430,
    "method": "paypal"
  },
  {
    "_id": "58159f13776b3c73f442f662",
    "amount": 5000,
    "method": "paypal"
  }
]

Think carefully about what we have just done.

  • A standalone app - donation-play-service - is running on localhost:9000 exposing a simple API
  • A separate app launches and issues a number of requests to this over http
  • The returned data is displayed on the console in Eclipse.

However, our client is Java, not Javascript. These classes we have produced here will be ideal starting points for building out the Android classes we need to hook up the donation-andoid app to the donation-web service.

Creating Donations

Try this at the end of the main method:

    Donation donation = new Donation(5000, "paypal");
    service.createDonation(candidates.get(0), donation);
    donations = service.getAllDonations();
    println ("--------------------");
    println ("  all donations with a new donation created: ");
    println ("--------------------");
    println(donations);

We are trying to create a donation. Our output should show the new donation at the end:

  {
    "_id": "5815a8bf0dc72a79a540cf20",
    "amount": 5000,
    "method": "paypal"
  }

Check the web app to see if it was created.

Donation Model

Looking again at the donation model:

public class Donation
{
  public String   _id;
  public int    amount;
  public String method;

  public Donation (int amount, String method)
  {
    this.amount = amount;
    this.method = method;
  }
}

.. and the equivalent mongoose model:

const donationSchema = mongoose.Schema({
  amount: Number,
  method: String,
  donor: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'User',
  },
  candidate:  {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Candidate',
  },
});

We can see we are missing the references to donor and candidate.

Include a new class in our java project now :

package app.donation.model;

public class DonationComplete
{
  public String   _id;
  public int    amount;
  public String method;
  public User donor;
  public Candidate candidate;

  public DonationComplete (int amount, String method)
  {
    this.amount = amount;
    this.method = method;
  }
}

It is identical to the previous version, except we have added references to donor and candidate. These references are using the Java User and Candidate classes we have already defined.

We will use this class in parellel with the existing simpler Donation class. First, include a new method in DonationService:

  @GET("/api/donations")
  Call<List<DonationComplete>> getAllCompleteDonations();

Then extend DonationServiceAPI with this new method:

  public List<DonationComplete> getAllCompleteDonations() throws Exception
  {
    Call<List<DonationComplete>> call = (Call<List<DonationComplete>>) service.getAllCompleteDonations();
    Response<List<DonationComplete>> donations = call.execute();
    return donations.body();
  }

At the end of our main method, try to read and print all donations:

    println ("--------------------");
    println ("  complete donations: ");
    println ("--------------------");
    List<DonationComplete> complateDonations = service.getAllCompleteDonations();
    println(complateDonations);

At the tail end of the console output, we see the complete donations, including the donor & candidate references:

[
  {
    "_id": "5815a5b10dc72a79a540cf1d",
    "amount": 40,
    "method": "paypal",
    "donor": {
      "_id": "5815a5b00dc72a79a540cf1a",
      "firstName": "Bart",
      "lastName": "Simpson",
      "email": "bart@simpson.com",
      "password": "secret"
    },
    "candidate": {
      "_id": "5815a5b00dc72a79a540cf1b",
      "firstName": "Lisa",
      "lastName": "Simpson",
      "office": "President"
    }
  },
  {
    "_id": "5815a5b10dc72a79a540cf1e",
    "amount": 90,
    "method": "direct",
    "donor": {
      "_id": "5815a5b00dc72a79a540cf19",
      "firstName": "Marge",
      "lastName": "Simpson",
      "email": "marge@simpson.com",
      "password": "secret"
    },
    "candidate": {
      "_id": "5815a5b00dc72a79a540cf1b",
      "firstName": "Lisa",
      "lastName": "Simpson",
      "office": "President"
    }
  },
  {
    "_id": "5815a5b10dc72a79a540cf1f",
    "amount": 430,
    "method": "paypal",
    "donor": {
      "_id": "5815a5b00dc72a79a540cf18",
      "firstName": "Homer",
      "lastName": "Simpson",
      "email": "homer@simpson.com",
      "password": "secret"
    },
    "candidate": {
      "_id": "5815a5b10dc72a79a540cf1c",
      "firstName": "Donald",
      "lastName": "Simpson",
      "office": "President"
    }
  },
  {
    "_id": "5815a8bf0dc72a79a540cf20",
    "amount": 5000,
    "method": "paypal",
    "candidate": {
      "_id": "5815a5b00dc72a79a540cf1b",
      "firstName": "Lisa",
      "lastName": "Simpson",
      "office": "President"
    }
  }
]

Exercises

Archive of the the project so far:

Exercise 1:

Introduce an endpoint that retrieves all donations for a specific candidate, and extend main to exercise it

Exercise 2:

Experiment with accessing a deployed version of the API.