Adding support for account and asset deserialization to the GetObjects API call wrapper

This commit is contained in:
Nelson R. Perez 2017-05-11 18:55:16 -05:00
parent 5ff6f29ebd
commit d21fea7cb6
5 changed files with 379 additions and 26 deletions

View file

@ -1,15 +1,23 @@
package de.bitsharesmunich.graphenej; package de.bitsharesmunich.graphenej;
import com.google.gson.*; import com.google.gson.JsonArray;
import de.bitsharesmunich.graphenej.interfaces.ByteSerializable; import com.google.gson.JsonDeserializationContext;
import de.bitsharesmunich.graphenej.interfaces.JsonSerializable; import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.DataOutput; import java.io.DataOutput;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.ArrayList; import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import de.bitsharesmunich.graphenej.interfaces.ByteSerializable;
import de.bitsharesmunich.graphenej.interfaces.JsonSerializable;
/** /**
* Class tha represents a graphene user account. * Class tha represents a graphene user account.
@ -18,8 +26,39 @@ import java.util.ArrayList;
public class UserAccount extends GrapheneObject implements ByteSerializable, JsonSerializable { public class UserAccount extends GrapheneObject implements ByteSerializable, JsonSerializable {
public static final String PROXY_TO_SELF = "1.2.5"; public static final String PROXY_TO_SELF = "1.2.5";
public static final String KEY_MEMBERSHIP_EXPIRATION_DATE = "membership_expiration_date";
public static final String KEY_REGISTRAR = "registrar";
public static final String KEY_REFERRER = "referrer";
public static final String KEY_LIFETIME_REFERRER = "lifetime_referrer";
public static final String KEY_NETWORK_FEE_PERCENTAGE = "network_fee_percentage";
public static final String KEY_LIFETIME_REFERRER_FEE_PERCENTAGE = "lifetime_referrer_fee_percentage";
public static final String KEY_REFERRER_REWARD_PERCENTAGE = "referrer_rewards_percentage";
public static final String KEY_NAME = "name";
public static final String KEY_OWNER = "owner";
public static final String KEY_ACTIVE = "active";
public static final String KEY_OPTIONS = "options";
public static final String KEY_STATISTICS = "statistics";
public static final String KEY_WHITELISTING_ACCOUNTS = "whitelisting_accounts";
public static final String KEY_BLACKLISTING_ACCOUNTS = "blacklisting_accounts";
public static final String KEY_WHITELISTED_ACCOUNTS = "whitelisted_accounts";
public static final String KEY_BLACKLISTED_ACCOUNTS = "blacklisted_accounts";
public static final String KEY_OWNER_SPECIAL_AUTHORITY = "owner_special_authority";
public static final String KEY_ACTIVE_SPECIAL_AUTHORITY = "active_special_authority";
public static final String KEY_N_CONTROL_FLAGS = "top_n_control_flags";
private long membershipExpirationDate;
private String registrar;
private String referrer;
private String lifetimeReferrer;
private long networkFeePercentage;
private long lifetimeReferrerFeePercentage;
private long referrerRewardsPercentage;
private String name;
private Authority owner;
private Authority active;
private AccountOptions options;
private String statistics;
private String accountName;
/** /**
* Constructor that expects a user account in the string representation. * Constructor that expects a user account in the string representation.
@ -37,24 +76,24 @@ public class UserAccount extends GrapheneObject implements ByteSerializable, Jso
*/ */
public UserAccount(String id, String name){ public UserAccount(String id, String name){
super(id); super(id);
this.accountName = name; this.name = name;
} }
/** /**
* Getter for the account name field. * Getter for the account name field.
* @return: The name of this account. * @return: The name of this account.
*/ */
public String getAccountName() { public String getName() {
return accountName; return name;
} }
/** /**
* Setter for the account name field. * Setter for the account name field.
* @param accountName: The account name. * @param name: The account name.
*/ */
public void setAccountName(String accountName) { public void setName(String name) {
this.accountName = accountName; this.name = name;
} }
@Override @Override
@ -94,6 +133,140 @@ public class UserAccount extends GrapheneObject implements ByteSerializable, Jso
return this.toJsonString(); return this.toJsonString();
} }
public long getMembershipExpirationDate() {
return membershipExpirationDate;
}
public void setMembershipExpirationDate(long membershipExpirationDate) {
this.membershipExpirationDate = membershipExpirationDate;
}
public String getRegistrar() {
return registrar;
}
public void setRegistrar(String registrar) {
this.registrar = registrar;
}
public String getReferrer() {
return referrer;
}
public void setReferrer(String referrer) {
this.referrer = referrer;
}
public String getLifetimeReferrer() {
return lifetimeReferrer;
}
public void setLifetimeReferrer(String lifetimeReferrer) {
this.lifetimeReferrer = lifetimeReferrer;
}
public long getNetworkFeePercentage() {
return networkFeePercentage;
}
public void setNetworkFeePercentage(long networkFeePercentage) {
this.networkFeePercentage = networkFeePercentage;
}
public long getLifetimeReferrerFeePercentage() {
return lifetimeReferrerFeePercentage;
}
public void setLifetimeReferrerFeePercentage(long lifetimeReferrerFeePercentage) {
this.lifetimeReferrerFeePercentage = lifetimeReferrerFeePercentage;
}
public long getReferrerRewardsPercentage() {
return referrerRewardsPercentage;
}
public void setReferrerRewardsPercentage(long referrerRewardsPercentage) {
this.referrerRewardsPercentage = referrerRewardsPercentage;
}
public Authority getOwner() {
return owner;
}
public void setOwner(Authority owner) {
this.owner = owner;
}
public Authority getActive() {
return active;
}
public void setActive(Authority active) {
this.active = active;
}
public AccountOptions getOptions() {
return options;
}
public void setOptions(AccountOptions options) {
this.options = options;
}
public String getStatistics() {
return statistics;
}
public void setStatistics(String statistics) {
this.statistics = statistics;
}
/**
* Deserializer used to build a UserAccount instance from the full JSON-formatted response obtained
* by the 'get_objects' API call.
*/
public static class UserAccountFullDeserializer implements JsonDeserializer<UserAccount> {
@Override
public UserAccount deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonAccount = json.getAsJsonObject();
SimpleDateFormat dateFormat = new SimpleDateFormat(Util.TIME_DATE_FORMAT);
// Retrieving and deserializing fields
String id = jsonAccount.get(KEY_ID).getAsString();
String name = jsonAccount.get(KEY_NAME).getAsString();
UserAccount userAccount = new UserAccount(id, name);
AccountOptions options = context.deserialize(jsonAccount.get(KEY_OPTIONS), AccountOptions.class);
Authority owner = context.deserialize(jsonAccount.get(KEY_OWNER), Authority.class);
Authority active = context.deserialize(jsonAccount.get(KEY_ACTIVE), Authority.class);
// Setting deserialized fields into the created instance
userAccount.setRegistrar(jsonAccount.get(KEY_REGISTRAR).getAsString());
// Handling the deserialization and assignation of the membership date, which internally
// is stored as a long POSIX time value
try{
Date date = dateFormat.parse(jsonAccount.get(KEY_MEMBERSHIP_EXPIRATION_DATE).getAsString());
userAccount.setMembershipExpirationDate(date.getTime());
} catch (ParseException e) {
System.out.println("ParseException. Msg: "+e.getMessage());
}
// Setting the other fields
userAccount.setReferrer(jsonAccount.get(KEY_REFERRER).getAsString());
userAccount.setLifetimeReferrer(jsonAccount.get(KEY_LIFETIME_REFERRER).getAsString());
userAccount.setNetworkFeePercentage(jsonAccount.get(KEY_NETWORK_FEE_PERCENTAGE).getAsLong());
userAccount.setLifetimeReferrerFeePercentage(jsonAccount.get(KEY_LIFETIME_REFERRER_FEE_PERCENTAGE).getAsLong());
userAccount.setReferrerRewardsPercentage(jsonAccount.get(KEY_REFERRER_REWARD_PERCENTAGE).getAsLong());
userAccount.setOwner(owner);
userAccount.setActive(active);
userAccount.setOptions(options);
userAccount.setStatistics(jsonAccount.get(KEY_STATISTICS).getAsString());
return userAccount;
}
}
/** /**
* Custom deserializer used to deserialize user accounts provided as response from the 'lookup_accounts' api call. * Custom deserializer used to deserialize user accounts provided as response from the 'lookup_accounts' api call.
* This response contains serialized user accounts in the form [[{id1},{name1}][{id1},{name1}]]. * This response contains serialized user accounts in the form [[{id1},{name1}][{id1},{name1}]].

View file

@ -1,15 +1,13 @@
package de.bitsharesmunich.graphenej.api; package de.bitsharesmunich.graphenej.api;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import com.neovisionaries.ws.client.WebSocket; import com.neovisionaries.ws.client.WebSocket;
import com.neovisionaries.ws.client.WebSocketFrame; import com.neovisionaries.ws.client.WebSocketFrame;
import de.bitsharesmunich.graphenej.AssetAmount;
import de.bitsharesmunich.graphenej.RPC;
import de.bitsharesmunich.graphenej.interfaces.WitnessResponseListener;
import de.bitsharesmunich.graphenej.models.ApiCall;
import de.bitsharesmunich.graphenej.models.BitAssetData;
import de.bitsharesmunich.graphenej.models.WitnessResponse;
import java.io.Serializable; import java.io.Serializable;
import java.lang.reflect.Type; import java.lang.reflect.Type;
@ -17,6 +15,18 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import de.bitsharesmunich.graphenej.AccountOptions;
import de.bitsharesmunich.graphenej.Asset;
import de.bitsharesmunich.graphenej.AssetAmount;
import de.bitsharesmunich.graphenej.Authority;
import de.bitsharesmunich.graphenej.GrapheneObject;
import de.bitsharesmunich.graphenej.RPC;
import de.bitsharesmunich.graphenej.UserAccount;
import de.bitsharesmunich.graphenej.interfaces.WitnessResponseListener;
import de.bitsharesmunich.graphenej.models.ApiCall;
import de.bitsharesmunich.graphenej.models.BitAssetData;
import de.bitsharesmunich.graphenej.models.WitnessResponse;
/** /**
* Created by nelson on 1/8/17. * Created by nelson on 1/8/17.
*/ */
@ -48,16 +58,40 @@ public class GetObjects extends BaseGrapheneHandler {
String response = frame.getPayloadText(); String response = frame.getPayloadText();
GsonBuilder gsonBuilder = new GsonBuilder(); GsonBuilder gsonBuilder = new GsonBuilder();
//TODO: Uncomment this line after the deserializer is implemented.
gsonBuilder.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer()); gsonBuilder.registerTypeAdapter(AssetAmount.class, new AssetAmount.AssetAmountDeserializer());
// gsonBuilder.registerTypeAdapter(BitAssetData.class, new BitAssetData.BitAssetDeserializer()); gsonBuilder.registerTypeAdapter(Asset.class, new Asset.AssetDeserializer());
gsonBuilder.registerTypeAdapter(UserAccount.class, new UserAccount.UserAccountFullDeserializer());
gsonBuilder.registerTypeAdapter(Authority.class, new Authority.AuthorityDeserializer());
gsonBuilder.registerTypeAdapter(AccountOptions.class, new AccountOptions.AccountOptionsDeserializer());
Gson gson = gsonBuilder.create();
// Only homogeneus array is currently supported List<GrapheneObject> parsedResult = new ArrayList<>();
if(ids.get(0).split("\\.")[1].equals("4")){
Type BitAssetDataType = new TypeToken<WitnessResponse<List<BitAssetData>>>(){}.getType(); JsonParser parser = new JsonParser();
WitnessResponse<List<BitAssetData>> witnessResponse = gsonBuilder.create().fromJson(response, BitAssetDataType); JsonArray resultArray = parser.parse(response).getAsJsonObject().get(WitnessResponse.KEY_RESULT).getAsJsonArray();
mListener.onSuccess(witnessResponse); for(JsonElement element : resultArray){
String id = element.getAsJsonObject().get(GrapheneObject.KEY_ID).getAsString();
GrapheneObject grapheneObject = new GrapheneObject(id);
switch (grapheneObject.getObjectType()){
case ASSET_OBJECT:
Asset asset = gson.fromJson(element, Asset.class);
parsedResult.add(asset);
break;
case ACCOUNT_OBJECT:
UserAccount account = gson.fromJson(element, UserAccount.class);
parsedResult.add(account);
break;
case ASSET_BITASSET_DATA:
Type BitAssetDataType = new TypeToken<WitnessResponse<List<BitAssetData>>>(){}.getType();
WitnessResponse<List<BitAssetData>> witnessResponse = gsonBuilder.create().fromJson(response, BitAssetDataType);
BitAssetData bitAssetData = witnessResponse.result.get(0);
parsedResult.add(bitAssetData);
}
} }
WitnessResponse<List<GrapheneObject>> output = new WitnessResponse<>();
output.result = parsedResult;
mListener.onSuccess(output);
websocket.disconnect(); websocket.disconnect();
} }

View file

@ -1,5 +1,6 @@
package de.bitsharesmunich.graphenej.models; package de.bitsharesmunich.graphenej.models;
import de.bitsharesmunich.graphenej.GrapheneObject;
import de.bitsharesmunich.graphenej.Price; import de.bitsharesmunich.graphenej.Price;
/** /**
@ -8,8 +9,7 @@ import de.bitsharesmunich.graphenej.Price;
* *
* Created by nelson on 1/8/17. * Created by nelson on 1/8/17.
*/ */
public class BitAssetData { public class BitAssetData extends GrapheneObject {
public String id;
public Object[] feeds; public Object[] feeds;
public AssetFeed current_feed; public AssetFeed current_feed;
public String current_feed_publication_time; public String current_feed_publication_time;
@ -18,4 +18,8 @@ public class BitAssetData {
public boolean is_prediction_market; public boolean is_prediction_market;
public Price settlement_price; public Price settlement_price;
public long settlement_fund; public long settlement_fund;
public BitAssetData(String id) {
super(id);
}
} }

View file

@ -4,5 +4,8 @@ package de.bitsharesmunich.graphenej.models;
* Generic witness response * Generic witness response
*/ */
public class WitnessResponse<T> extends BaseResponse{ public class WitnessResponse<T> extends BaseResponse{
public static final String KEY_ID = "id";
public static final String KEY_RESULT = "result";
public T result; public T result;
} }

View file

@ -0,0 +1,139 @@
package de.bitsharesmunich.graphenej.api;
import com.neovisionaries.ws.client.WebSocketException;
import junit.framework.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import de.bitsharesmunich.graphenej.Asset;
import de.bitsharesmunich.graphenej.GrapheneObject;
import de.bitsharesmunich.graphenej.UserAccount;
import de.bitsharesmunich.graphenej.interfaces.WitnessResponseListener;
import de.bitsharesmunich.graphenej.models.BaseResponse;
import de.bitsharesmunich.graphenej.models.BitAssetData;
import de.bitsharesmunich.graphenej.models.WitnessResponse;
/**
* Testing the {@link GetObjects} API wrapper and its deserialization
*
* Created by nelson on 5/10/17.
*/
public class GetObjectsTest extends BaseApiTest{
private final Asset asset = new Asset("1.3.0", "BTS", 5);
private final UserAccount account = new UserAccount("1.2.116354");
private final String bitAssetId = "2.4.13";
@Test
public void testGetAsset(){
try{
ArrayList<String> ids = new ArrayList<>();
ids.add(asset.getObjectId());
mWebSocket.addListener(new GetObjects(ids, new WitnessResponseListener() {
@Override
public void onSuccess(WitnessResponse response) {
System.out.println("onSuccess");
List<GrapheneObject> result = (List<GrapheneObject>) response.result;
System.out.println("Got " + result.size() + " result");
Assert.assertEquals("Making sure we only get one address back", 1, result.size());
synchronized (GetObjectsTest.this){
GetObjectsTest.this.notifyAll();
}
}
@Override
public void onError(BaseResponse.Error error) {
System.out.println("onError");
synchronized (GetObjectsTest.this){
GetObjectsTest.this.notifyAll();
}
}
}));
mWebSocket.connect();
synchronized (this){
wait();
}
}catch (WebSocketException e) {
System.out.println("WebSocketException. Msg: " + e.getMessage());
} catch (InterruptedException e) {
System.out.println("InterruptedException. Msg: "+e.getMessage());
}
}
@Test
public void testGetAccount(){
try{
ArrayList<String> ids = new ArrayList<>();
ids.add(account.getObjectId());
mWebSocket.addListener(new GetObjects(ids, new WitnessResponseListener() {
@Override
public void onSuccess(WitnessResponse response) {
System.out.println("onSuccess");
List<GrapheneObject> result = (List<GrapheneObject>) response.result;
UserAccount userAccount = (UserAccount) result.get(0);
System.out.println("Account name: "+userAccount.getName());
System.out.println("json string: "+userAccount.toJsonString());
System.out.println("owner: "+userAccount.getOwner().getKeyAuthList().get(0).getAddress());
System.out.println("active: "+userAccount.getActive().getKeyAuthList().get(0).getAddress());
System.out.println("memo: "+userAccount.getOptions().getMemoKey().getAddress());
synchronized (GetObjectsTest.this){
GetObjectsTest.this.notifyAll();
}
}
@Override
public void onError(BaseResponse.Error error) {
System.out.println("onError");
synchronized (GetObjectsTest.this){
GetObjectsTest.this.notifyAll();
}
}
}));
mWebSocket.connect();
synchronized (this){
wait();
}
}catch (WebSocketException e) {
System.out.println("WebSocketException. Msg: " + e.getMessage());
} catch (InterruptedException e) {
System.out.println("InterruptedException. Msg: "+e.getMessage());
}
}
@Test
public void testBitAssetData(){
try{
ArrayList<String> ids = new ArrayList<>();
ids.add(bitAssetId);
mWebSocket.addListener(new GetObjects(ids, new WitnessResponseListener() {
@Override
public void onSuccess(WitnessResponse response) {
System.out.println("onSuccess");
List<GrapheneObject> list = (List<GrapheneObject>) response.result;
BitAssetData bitAssetData = (BitAssetData) list.get(0);
System.out.println("feed time: " + bitAssetData.current_feed_publication_time);
}
@Override
public void onError(BaseResponse.Error error) {
System.out.println("onError");
}
}));
mWebSocket.connect();
synchronized (this){
wait();
}
}catch (WebSocketException e) {
System.out.println("WebSocketException. Msg: " + e.getMessage());
} catch (InterruptedException e) {
System.out.println("InterruptedException. Msg: "+e.getMessage());
} }
}