com.mifos.services.API.java Source code

Java tutorial

Introduction

Here is the source code for com.mifos.services.API.java

Source

/*
 * This project is licensed under the open source MPL V2.
 * See https://github.com/openMF/android-client/blob/master/LICENSE.md
 */

package com.mifos.services;

import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
import android.widget.Toast;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.mifos.objects.SearchedEntity;
import com.mifos.objects.User;
import com.mifos.objects.accounts.ClientAccounts;
import com.mifos.objects.accounts.loan.LoanApprovalRequest;
import com.mifos.objects.accounts.loan.LoanRepaymentRequest;
import com.mifos.objects.accounts.loan.LoanRepaymentResponse;
import com.mifos.objects.accounts.loan.LoanWithAssociations;
import com.mifos.objects.accounts.savings.SavingsAccountTransactionRequest;
import com.mifos.objects.accounts.savings.SavingsAccountTransactionResponse;
import com.mifos.objects.accounts.savings.SavingsAccountWithAssociations;
import com.mifos.objects.client.Client;
import com.mifos.objects.client.Page;
import com.mifos.objects.db.CollectionSheet;
import com.mifos.objects.db.OfflineCenter;
import com.mifos.objects.group.Center;
import com.mifos.objects.group.CenterWithAssociations;
import com.mifos.objects.group.Group;
import com.mifos.objects.group.GroupWithAssociations;
import com.mifos.objects.noncore.DataTable;
import com.mifos.objects.noncore.Document;
import com.mifos.objects.noncore.Identifier;
import com.mifos.objects.organisation.Office;
import com.mifos.objects.organisation.Staff;
import com.mifos.objects.templates.loans.LoanRepaymentTemplate;
import com.mifos.objects.templates.savings.SavingsAccountTransactionTemplate;
import com.mifos.services.data.APIEndPoint;
import com.mifos.services.data.ClientPayload;
import com.mifos.services.data.CollectionSheetPayload;
import com.mifos.services.data.GpsCoordinatesRequest;
import com.mifos.services.data.GpsCoordinatesResponse;
import com.mifos.services.data.Payload;
import com.mifos.services.data.SaveResponse;
import com.mifos.utils.Constants;
import com.mifos.utils.MFErrorResponse;
import com.squareup.okhttp.OkHttpClient;

import org.apache.http.HttpStatus;

import java.security.cert.CertificateException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import retrofit.Callback;
import retrofit.ErrorHandler;
import retrofit.RequestInterceptor;
import retrofit.RestAdapter;
import retrofit.RetrofitError;
import retrofit.client.OkClient;
import retrofit.client.Response;
import retrofit.http.Body;
import retrofit.http.DELETE;
import retrofit.http.GET;
import retrofit.http.Headers;
import retrofit.http.Multipart;
import retrofit.http.POST;
import retrofit.http.PUT;
import retrofit.http.Part;
import retrofit.http.Path;
import retrofit.http.Query;
import retrofit.http.QueryMap;
import retrofit.mime.TypedFile;
import retrofit.mime.TypedString;

public class API {
    public static final String TAG = API.class.getName();
    public static final String ACCEPT_JSON = "Accept: application/json";
    public static final String CONTENT_TYPE_JSON = "Content-Type: application/json";
    public static final String CONTENT_TYPE_MULTIPART_FORM_DATA = "Content-Type: multipart/form-data";
    public static final String HEADER_AUTHORIZATION = "Authorization";
    public static final String INSTANCE_URL = "InstanceUrl";

    /*
    As Mifos is a multi-tenant platform, all requests require you to specify a tenant
    as a header in each request.
     */
    public static final String HEADER_MIFOS_TENANT_ID = "X-Mifos-Platform-TenantId";
    //This instance has more Data for Testing
    public static String mInstanceUrl = "https://demo.openmf.org/mifosng-provider/api/v1";
    public static String mTenantIdentifier = "default";
    public CenterService centerService;
    public ClientAccountsService clientAccountsService;
    public ClientService clientService;
    public DataTableService dataTableService;
    public LoanService loanService;
    public SavingsAccountService savingsAccountService;
    public SearchService searchService;
    public UserAuthService userAuthService;
    public GpsCoordinatesService gpsCoordinatesService;
    public GroupService groupService;
    public DocumentService documentService;
    public IdentifierService identifierService;
    public OfficeService officeService;
    public StaffService staffService;

    public API(final String url, final String tenantIdentifier, boolean shouldByPassSSLSecurity) {

        RestAdapter.Builder restAdapterBuilder = new RestAdapter.Builder();
        restAdapterBuilder.setEndpoint(url);
        if (shouldByPassSSLSecurity) {
            restAdapterBuilder.setClient(new OkClient(getUnsafeOkHttpClient()));
        }
        restAdapterBuilder.setRequestInterceptor(new RequestInterceptor() {
            @Override
            public void intercept(RequestFacade request) {
                if (tenantIdentifier != null && !tenantIdentifier.isEmpty()) {
                    request.addHeader(HEADER_MIFOS_TENANT_ID, tenantIdentifier);
                } else {
                    request.addHeader(HEADER_MIFOS_TENANT_ID, "default");
                }

                /*
                    Look for the Auth token in the shared preferences
                    and add it to the request. Because it is mandatory to
                    supply the Authorization Header in every request
                */

                SharedPreferences pref = PreferenceManager
                        .getDefaultSharedPreferences(Constants.applicationContext);
                String authToken = pref.getString(User.AUTHENTICATION_KEY, "NA");

                if (authToken != null && !"NA".equals(authToken)) {
                    request.addHeader(HEADER_AUTHORIZATION, authToken);
                }

            }
        });
        restAdapterBuilder.setErrorHandler(new MifosRestErrorHandler());
        RestAdapter restAdapter = restAdapterBuilder.build();
        // TODO: This logging is sometimes excessive, e.g. for client image requests.
        restAdapter.setLogLevel(RestAdapter.LogLevel.FULL);

        centerService = restAdapter.create(CenterService.class);
        clientService = restAdapter.create(ClientService.class);
        clientAccountsService = restAdapter.create(ClientAccountsService.class);
        dataTableService = restAdapter.create(DataTableService.class);
        loanService = restAdapter.create(LoanService.class);
        savingsAccountService = restAdapter.create(SavingsAccountService.class);
        searchService = restAdapter.create(SearchService.class);
        userAuthService = restAdapter.create(UserAuthService.class);
        gpsCoordinatesService = restAdapter.create(GpsCoordinatesService.class);
        groupService = restAdapter.create(GroupService.class);
        documentService = restAdapter.create(DocumentService.class);
        identifierService = restAdapter.create(IdentifierService.class);
        officeService = restAdapter.create(OfficeService.class);
        staffService = restAdapter.create(StaffService.class);
    }

    private OkHttpClient getUnsafeOkHttpClient() {
        try {
            // Create a trust manager that does not validate certificate chains
            final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
                @Override
                public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType)
                        throws CertificateException {
                }

                @Override
                public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType)
                        throws CertificateException {
                }

                @Override
                public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
            } };

            // Install the all-trusting trust manager
            final SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
            // Create an ssl socket factory with our all-trusting manager
            final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

            OkHttpClient okHttpClient = new OkHttpClient();
            okHttpClient.setSslSocketFactory(sslSocketFactory);
            okHttpClient.setHostnameVerifier(new HostnameVerifier() {
                @Override
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            });

            return okHttpClient;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static <T> Callback<T> getCallback(T t) {
        Callback<T> cb = new Callback<T>() {
            @Override
            public void success(T o, Response response) {
                System.out.println("Object " + o);
            }

            @Override
            public void failure(RetrofitError retrofitError) {
                System.out.println("Error: " + retrofitError);
            }
        };

        return cb;
    }

    public static <T> Callback<List<T>> getCallbackList(List<T> t) {
        Callback<List<T>> cb = new Callback<List<T>>() {
            @Override
            public void success(List<T> o, Response response) {
                System.out.println("Object " + o);
            }

            @Override
            public void failure(RetrofitError retrofitError) {
                System.out.println("Error: " + retrofitError);
            }
        };

        return cb;
    }

    public interface CenterService {

        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @GET(APIEndPoint.CENTERS)
        public void getAllCenters(Callback<List<Center>> callback);

        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @GET(APIEndPoint.CENTERS + "/{centerId}?associations=groupMembers,collectionMeetingCalendar")
        public void getCenterWithGroupMembersAndCollectionMeetingCalendar(@Path("centerId") int centerId,
                Callback<CenterWithAssociations> centerWithAssociationsCallback);

        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @GET(APIEndPoint.CENTERS)
        public void getAllCentersInOffice(@Query("officeId") int officeId,
                @QueryMap Map<String, Object> additionalParams, Callback<List<Center>> centersCallback);

        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @GET(APIEndPoint.CENTERS + "/{centerId}?associations=groupMembers")
        public void getAllGroupsForCenter(@Path("centerId") int centerId,
                Callback<CenterWithAssociations> centerWithAssociationsCallback);

        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @POST(APIEndPoint.CENTERS + "/{centerId}?command=generateCollectionSheet")
        public void getCollectionSheet(@Path("centerId") long centerId, @Body Payload payload,
                Callback<CollectionSheet> callback);

        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @POST(APIEndPoint.CENTERS + "/{centerId}?command=saveCollectionSheet")
        public SaveResponse saveCollectionSheet(@Path("centerId") int centerId,
                @Body CollectionSheetPayload collectionSheetPayload);

        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @POST(APIEndPoint.CENTERS + "/{centerId}?command=saveCollectionSheet")
        public void saveCollectionSheet(@Path("centerId") int centerId,
                @Body CollectionSheetPayload collectionSheetPayload, Callback<SaveResponse> saveResponseCallback);

        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @POST(APIEndPoint.CLIENTS + "")
        public void uploadNewClientDetails();

        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @GET(APIEndPoint.CENTERS)
        public void getCenterList(@Query("dateFormat") String dateFormat, @Query("locale") String locale,
                @Query("meetingDate") String meetingDate, @Query("officeId") int officeId,
                @Query("staffId") int staffId, Callback<List<OfflineCenter>> callback);

    }

    public interface ClientAccountsService {

        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @GET(APIEndPoint.CLIENTS + "/{clientId}/accounts")
        public void getAllAccountsOfClient(@Path("clientId") int clientId,
                Callback<ClientAccounts> clientAccountsCallback);

    }

    public interface ClientService {

        //This is a default call and Loads client from 0 to 200
        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @GET(APIEndPoint.CLIENTS)
        public void listAllClients(Callback<Page<Client>> callback);

        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @GET(APIEndPoint.CLIENTS)
        public void listAllClients(@Query("offset") int offset, @Query("limit") int limit,
                Callback<Page<Client>> callback);

        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @GET(APIEndPoint.CLIENTS + "/{clientId}")
        public void getClient(@Path("clientId") int clientId, Callback<Client> clientCallback);

        @Multipart
        @POST(APIEndPoint.CLIENTS + "/{clientId}/images")
        public void uploadClientImage(@Path("clientId") int clientId, @Part("file") TypedFile file,
                Callback<Response> responseCallback);

        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @DELETE(APIEndPoint.CLIENTS + "/{clientId}/images")
        void deleteClientImage(@Path("clientId") int clientId, Callback<Response> responseCallback);

        //TODO: Implement when API Fixed
        //@Headers({"Accept: application/octet-stream", CONTENT_TYPE_JSON})
        @GET("/clients/{clientId}/images")
        public void getClientImage(@Path("clientId") int clientId, Callback<TypedString> callback);

        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @POST(APIEndPoint.CLIENTS)
        void createClient(@Body ClientPayload clientPayload, Callback<Client> callback);
    }

    public interface SearchService {

        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @GET(APIEndPoint.SEARCH + "?resource=clients")
        public void searchClientsByName(@Query("query") String clientName,
                Callback<List<SearchedEntity>> listCallback);
    }

    public interface LoanService {

        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @GET(APIEndPoint.LOANS + "/{loanId}?associations=all")
        public void getLoanByIdWithAllAssociations(@Path("loanId") int loanId,
                Callback<LoanWithAssociations> loanCallback);

        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @GET(APIEndPoint.LOANS + "/{loanId}/transactions/template?command=repayment")
        public void getLoanRepaymentTemplate(@Path("loanId") int loanId,
                Callback<LoanRepaymentTemplate> loanRepaymentTemplateCallback);

        /*
         Mandatory Fields
            1. String approvedOnDate
        */
        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @POST(APIEndPoint.LOANS + "/{loanId}?command=approve")
        public void approveLoanApplication(@Path("loanId") int loanId,
                @Body LoanApprovalRequest loanApprovalRequest, Callback<GenericResponse> genericResponseCallback);

        /*
        Mandatory Fields
            1. String actualDisbursementDate
          */
        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @POST(APIEndPoint.LOANS + "/{loanId}/?command=disburse")
        public void disburseLoan(@Path("loanId") int loanId, @Body HashMap<String, Object> genericRequest,
                Callback<GenericResponse> genericResponseCallback);

        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @POST(APIEndPoint.LOANS + "/{loanId}/transactions?command=repayment")
        public void submitPayment(@Path("loanId") int loanId, @Body LoanRepaymentRequest loanRepaymentRequest,
                Callback<LoanRepaymentResponse> loanRepaymentResponseCallback);

        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @GET(APIEndPoint.LOANS + "/{loanId}?associations=repaymentSchedule")
        public void getLoanRepaymentSchedule(@Path("loanId") int loanId,
                Callback<LoanWithAssociations> loanWithRepaymentScheduleCallback);

        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @GET(APIEndPoint.LOANS + "/{loanId}?associations=transactions")
        public void getLoanWithTransactions(@Path("loanId") int loanId,
                Callback<LoanWithAssociations> loanWithAssociationsCallback);
    }

    public interface SavingsAccountService {

        /**
         *
         * @param savingsAccountId - savingsAccountId for which information is requested
         * @param association - Mention Type of Association Needed, Like :- all, transactions etc.
         * @param savingsAccountWithAssociationsCallback - callback to receive the response
         *
         * Use this method to retrieve the Savings Account With Associations
         */
        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @GET("/{savingsAccountType}/{savingsAccountId}")
        public void getSavingsAccountWithAssociations(@Path("savingsAccountType") String savingsAccountType,
                @Path("savingsAccountId") int savingsAccountId, @Query("associations") String association,
                Callback<SavingsAccountWithAssociations> savingsAccountWithAssociationsCallback);

        /**
         *
         * @param savingsAccountId - savingsAccountId for which information is requested
         * @param savingsAccountTransactionTemplateCallback - Savings Account Transaction Template Callback
         *
         * Use this method to retrieve the Savings Account Transaction Template
         */
        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @GET("/{savingsAccountType}/{savingsAccountId}/transactions/template")
        public void getSavingsAccountTransactionTemplate(@Path("savingsAccountType") String savingsAccountType,
                @Path("savingsAccountId") int savingsAccountId, @Query("command") String transactionType,
                Callback<SavingsAccountTransactionTemplate> savingsAccountTransactionTemplateCallback);

        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @POST("/{savingsAccountType}/{savingsAccountId}/transactions")
        public void processTransaction(@Path("savingsAccountType") String savingsAccountType,
                @Path("savingsAccountId") int savingsAccountId, @Query("command") String transactionType,
                @Body SavingsAccountTransactionRequest savingsAccountTransactionRequest,
                Callback<SavingsAccountTransactionResponse> savingsAccountTransactionResponseCallback);

    }

    public interface DataTableService {

        //TODO refactor the methods to to just one and specify the Query param

        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @GET(APIEndPoint.DATATABLES + "?apptable=m_savings_account")
        public void getDatatablesOfSavingsAccount(Callback<List<DataTable>> listCallback);

        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @GET(APIEndPoint.DATATABLES + "?apptable=m_client")
        public void getDatatablesOfClient(Callback<List<DataTable>> listCallback);

        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @GET(APIEndPoint.DATATABLES + "?apptable=m_loan")
        public void getDatatablesOfLoan(Callback<List<DataTable>> listCallback);

        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @GET(APIEndPoint.DATATABLES + "/{dataTableName}/{entityId}/")
        public void getDataOfDataTable(@Path("dataTableName") String dataTableName, @Path("entityId") int entityId,
                Callback<JsonArray> jsonArrayCallback);

        //TODO Improve Body Implementation with Payload
        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @POST(APIEndPoint.DATATABLES + "/{dataTableName}/{entityId}/")
        public void createEntryInDataTable(@Path("dataTableName") String dataTableName,
                @Path("entityId") int entityId, @Body Map<String, Object> requestPayload,
                Callback<GenericResponse> callback);

        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @DELETE(APIEndPoint.DATATABLES + "/{dataTableName}/{entityId}/{dataTableRowId}")
        public void deleteEntryOfDataTableManyToMany(@Path("dataTableName") String dataTableName,
                @Path("entityId") int entityId, @Path("dataTableRowId") int dataTableRowId,
                Callback<GenericResponse> callback);

    }

    /**
     * Service for management of Documents
     */

    public interface DocumentService {

        @GET("/{entityType}/{entityId}" + APIEndPoint.DOCUMENTS)
        @Headers({ CONTENT_TYPE_JSON })
        public void getListOfDocuments(@Path("entityType") String entityType, @Path("entityId") int entityId,
                Callback<List<Document>> documentListCallback);

        /**
         *
         * @param entityType - Type for which document is being uploaded (Client, Loan or Savings etc)
         * @param entityId - Id of Entity
         * @param nameOfDocument - Document Name
         * @param description - Mandatory - Document Description
         * @param typedFile - Mandatory
         * @param genericResponseCallback - Response Callback
         */
        @POST("/{entityType}/{entityId}" + APIEndPoint.DOCUMENTS)
        //For some reason you cannot set these headers for this request.
        //@Headers({ACCEPT_JSON, CONTENT_TYPE_MULTIPART_FORM_DATA})
        @Multipart
        public void createDocument(@Path("entityType") String entityType, @Path("entityId") int entityId,
                @Part("name") String nameOfDocument, @Part("description") String description,
                @Part("file") TypedFile typedFile, Callback<GenericResponse> genericResponseCallback);

    }

    /**
     * Service for management of Identifiers
     */

    public interface IdentifierService {

        @GET(APIEndPoint.CLIENTS + "/{clientId}" + APIEndPoint.IDENTIFIERS)
        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        public void getListOfIdentifiers(@Path("clientId") int clientId,
                Callback<List<Identifier>> identifierListCallback);

        @DELETE(APIEndPoint.CLIENTS + "/{clientId}" + APIEndPoint.IDENTIFIERS + "/{identifierId}")
        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        public void deleteIdentifier(@Path("clientId") int clientId, @Path("identifierId") int identifierId,
                Callback<GenericResponse> genericResponseCallback);

    }

    /**
     * Service for authenticating users.
     * No other service can be used without authentication.
     */

    public interface UserAuthService {

        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        @POST(APIEndPoint.AUTHENTICATION)
        public void authenticate(@Query("username") String username, @Query("password") String password,
                Callback<User> userCallback);

    }

    /**
     * Service for getting and retrieving GPS coordinates for a client's location, stored
     * in a custom data table.
     */
    public interface GpsCoordinatesService {

        @POST(APIEndPoint.DATATABLES + "/gps_coordinates/{clientId}")
        public void setGpsCoordinates(@Path("clientId") int clientId,
                @Body GpsCoordinatesRequest gpsCoordinatesRequest,
                Callback<GpsCoordinatesResponse> gpsCoordinatesResponseCallback);

        @PUT(APIEndPoint.DATATABLES + "/gps_coordinates/{clientId}")
        public void updateGpsCoordinates(@Path("clientId") int clientId,
                @Body GpsCoordinatesRequest gpsCoordinatesRequest,
                Callback<GpsCoordinatesResponse> gpsCoordinatesResponseCallback);

    }

    public interface GroupService {

        @GET(APIEndPoint.GROUPS + "/{groupId}?associations=all")
        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        public void getGroupWithAssociations(@Path("groupId") int groupId,
                Callback<GroupWithAssociations> groupWithAssociationsCallback);

        @GET(APIEndPoint.GROUPS)
        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        public void getAllGroupsInOffice(@Query("officeId") int officeId, @QueryMap Map<String, Object> params,
                Callback<List<Group>> listOfGroupsCallback);

    }

    /**
     * Serivce for interacting with Offices
     */
    public interface OfficeService {

        /**
         * Fetches List of All the Offices
         * @param listOfOfficesCallback
         */
        @GET(APIEndPoint.OFFICES)
        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        public void getAllOffices(Callback<List<Office>> listOfOfficesCallback);
    }

    public interface StaffService {

        @GET(APIEndPoint.STAFF + "?status=all")
        @Headers({ ACCEPT_JSON, CONTENT_TYPE_JSON })
        public void getStaffForOffice(@Query("officeId") int officeId, Callback<List<Staff>> staffListCallback);

    }

    static class MifosRestErrorHandler implements ErrorHandler {
        @Override
        public Throwable handleError(RetrofitError retrofitError) {

            Response response = retrofitError.getResponse();
            if (response != null) {

                if (response.getStatus() == HttpStatus.SC_UNAUTHORIZED) {
                    Log.e("Status", "Authentication Error.");

                } else if (response.getStatus() == HttpStatus.SC_BAD_REQUEST) {

                    MFErrorResponse mfErrorResponse = new Gson().fromJson(response.getBody().toString(),
                            MFErrorResponse.class);
                    Log.d("Status", "Bad Request - Invalid Parameter or Data Integrity Issue.");
                    Log.d("URL", response.getUrl());
                    List<retrofit.client.Header> headersList = response.getHeaders();
                    Iterator<retrofit.client.Header> iterator = headersList.iterator();
                    while (iterator.hasNext()) {
                        retrofit.client.Header header = iterator.next();
                        Log.d("Header ", header.toString());
                    }
                } else if (response.getStatus() == HttpStatus.SC_FORBIDDEN) {

                    MFErrorResponse mfErrorResponse = new Gson().fromJson(response.getBody().toString(),
                            MFErrorResponse.class);

                    Toast.makeText(Constants.applicationContext, mfErrorResponse.getDefaultUserMessage(),
                            Toast.LENGTH_LONG).show();

                    Log.d("Status", "Bad Request - Invalid Parameter or Data Integrity Issue.");
                    Log.d("URL", response.getUrl());
                    List<retrofit.client.Header> headersList = response.getHeaders();
                    Iterator<retrofit.client.Header> iterator = headersList.iterator();
                    while (iterator.hasNext()) {
                        retrofit.client.Header header = iterator.next();
                        Log.d("Header ", header.toString());
                    }

                }

            }

            return retrofitError;
        }

    }
}