Steem API-Wrapper for Java - V0.0.2 Released #Update2

in #steemit8 years ago (edited)

Hay Steemers,

I've just wanted to share the progress on the Steem API-Wrapper project with you. In the last two days I've added about 3000 lines of code to the project and decided to release a new version.

For those who want to use the project or even better, want to contribute to it, the source code can be found at github.

API
Source

What's new?

There are a bunch of methods which are implemented now, some small fixes and some imprvoed behavior.

  • The wrapper is now able to handle the different apis (Initially, only the database_api was supported )
  • You will be informed about which apis are not enabled on the node you are connected to.

    13:05:07.910 [main] WARN eu.bittrade.libs.steem.api.wrapper.SteemApiWrapper - The TAGS_API is not published by the configured node.
    13:05:08.024 [main] WARN eu.bittrade.libs.steem.api.wrapper.SteemApiWrapper - The NETWORK_NODE_API is not published by the configured node.
    13:05:08.138 [main] WARN eu.bittrade.libs.steem.api.wrapper.SteemApiWrapper - The NETWORK_BROADCAST_API is not published by the configured node.

  • It can now handle errors returned by the steem node.
  • The return types of the methods have been improved.
    Before: int accounts = api.getAccountCount().getCount()
    Now: int accounts = getAccountCount()
  • A bunch of api methods is now supported (see the detailed list below)
  • The example provided by the README on github (also attached below), is now a little bit cleaner.

Future plans

As you can see in the list below, there are still a bunch of methods missing. Also, some apis like the network_broadcast_api are not accable by the most nodes which makes it hard to test. Due to that, these are the plans for the upcoming days:

  • Deploy the project to the maven central repository, to allow an easy integration in other projects.
  • Create my own Node to (atleast) test the apis.
  • Implement and test more methods.

If you don't want to miss an update, make sure to follow me here:

alt text
Source

Thank you and best regards!

List of implemented methods and known problems

database_api

MethodImplementedKnown problems
set_subscribe_callbackNo-
set_pending_transaction_callbackNo-
set_block_applied_callbackNo-
cancel_all_subscriptionsNo-
get_trending_tagsYes-
get_discussions_by_trendingNo-
get_discussions_by_trending30No-
get_discussions_by_createdNo-
get_discussions_by_activeYes-
get_discussions_by_cashoutNo-
get_discussions_by_payoutNo-
get_discussions_by_votesNo-
get_discussions_by_childrenNo-
get_discussions_by_hotNo-
get_discussions_by_feedNo-
get_discussions_by_blogNo-
get_discussions_by_commentsNo-
get_discussions_by_promotedNo-
get_block_headerNo-
get_blockNo-
get_stateNo-
get_trending_categoriesNo-
get_best_categoriesNo-
get_active_categoriesNo-
get_recent_categoriesNo-
get_configYes-
get_dynamic_global_propertiesYes-
get_chain_propertiesYes-
get_feed_historyNo-
get_current_median_history_priceYes-
get_witness_scheduleYes-
get_hardfork_versionYes-
get_next_scheduled_hardforkNo-
get_key_referencesNo-
get_accountsNo-
get_account_referencesNo-
lookup_account_namesNo-
lookup_accountsYes-
get_account_countYes-
get_conversion_requestsNo-
get_account_historyYes-
get_owner_historyNo-
get_recovery_requestNo-
get_escrowNo-
get_withdraw_routesNo-
get_savings_withdraw_fromNo-
get_savings_withdraw_toNo-
get_order_bookNo-
get_open_ordersNo-
get_liquidity_queueNo-
get_transaction_hexNo-
get_transactionNo-
get_required_signaturesNo-
get_potential_signaturesNo-
verify_authorityNo-
verify_account_authorityNo-
get_active_votesYes-
get_account_votesYes-
get_contentYes-
get_content_repliesYes-
get_discussions_by_author_before_dateNo-
get_replies_by_last_updateNo-
get_witnessesYes-
get_witness_by_accountNo-
get_witnesses_by_voteNo-
lookup_witness_accountsYes-
get_witness_countNo-
get_active_witnessesNo-
get_miner_queueYes-

network_broadcast_api

MethodImplementedKnown problems
broadcast_transactionNo-
broadcast_transaction_with_callbackNo-
broadcast_transaction_synchronousNo-
broadcast_blockNo-
set_max_block_ageNo-

network_node_api

MethodImplementedKnown problems
get_infoNo-
add_nodeNo-
get_connected_peersNo-
get_potential_peersNo-
get_advanced_node_parametersNo-
set_advanced_node_parametersNo-

login_api

MethodImplementedKnown problems
loginYesThe web socket api returns always true
get_api_by_nameYes-
get_versionYes-

New Example-Code

package eu.bittrade.libs.steem.api.wrapper;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import eu.bittrade.libs.steem.api.wrapper.configuration.SteemApiWrapperConfig;
import eu.bittrade.libs.steem.api.wrapper.exceptions.SteemConnectionException;
import eu.bittrade.libs.steem.api.wrapper.exceptions.SteemResponseError;
import eu.bittrade.libs.steem.api.wrapper.exceptions.SteemTimeoutException;
import eu.bittrade.libs.steem.api.wrapper.exceptions.SteemTransformationException;
import eu.bittrade.libs.steem.api.wrapper.models.ActiveVote;
import eu.bittrade.libs.steem.api.wrapper.models.Vote;

public class SteemAPIUsageExample {
    private static final Logger LOGGER = LogManager.getLogger(SteemAPIUsageExample.class);

    public static void main(String args[]) {
        // Create a config object that already provides the default settings.
        SteemApiWrapperConfig myConfig = new SteemApiWrapperConfig();

        // Change the default settings if needed.
        try {
            myConfig.setWebsocketEndpointURI(new URI("wss://steemit.com/wspa"));
        } catch (URISyntaxException e) {
            throw new RuntimeException("The given URI is not valid.", e);
        }

        try {
            // Create a new apiWrapper with your config object.
            SteemApiWrapper steemApiWrapper = new SteemApiWrapper(myConfig);

            // Get the votes done by the specified account:
            List<Vote> votes = steemApiWrapper.getAccountVotes("dez1337");
            LOGGER.info("The user dez1337 has done {} votes so far.", votes.size());
            LOGGER.info("His last vote has been done on {}.", votes.get(votes.size() - 1).getTime());
            int numberOfAccounts = steemApiWrapper.getAccountCount();
            int numberOfWitnesses = steemApiWrapper.getWitnessCount();
            LOGGER.info(
                    "dez1337 is one of {} accounts on steemit and may increase the number witnesses to {} in the near future.",
                    numberOfAccounts, numberOfWitnesses + 1);

            List<ActiveVote> activeVotesForArticle = steemApiWrapper.getActiveVotes("dez1337",
                    "steem-api-wrapper-for-java-update1");
            LOGGER.info("The last vote for a article of dez1337 has been done from {}.",
                    activeVotesForArticle.get(activeVotesForArticle.size() - 1).getVoter());
            LOGGER.info(
                    "You may also want to vote for some posts to generate some Steem which is currently worth about {}.",
                    steemApiWrapper.getCurrentMedianHistoryPrice().getBase());
            // Force an error response:
            steemApiWrapper.getAccountVotes("thisAcountDoesNotExistYet");
        } catch (SteemConnectionException | SteemTimeoutException | SteemTransformationException e) {
            LOGGER.error("Error!", e);
        } catch (SteemResponseError e) {
            // The SteemResponseError contains the error response.
            LOGGER.error("An error with code {} occured with the following message {}.", e.getError().getSteemErrorDetails().getData().getCode(), e.getError().getSteemErrorDetails().getMessage());
        }
    }
}

Output:

13:05:07.161 [Grizzly(1 | No | -] INFO eu.bittrade.libs.steem.api.wrapper.communication.SteemEndpoint - Connection has been established.
13:05:07.910 [main] WARN eu.bittrade.libs.steem.api.wrapper.SteemApiWrapper - The TAGS_API is not published by the configured node.
13:05:08.024 [main] WARN eu.bittrade.libs.steem.api.wrapper.SteemApiWrapper - The NETWORK_NODE_API is not published by the configured node.
13:05:08.138 [main] WARN eu.bittrade.libs.steem.api.wrapper.SteemApiWrapper - The NETWORK_BROADCAST_API is not published by the configured node.
13:05:08.793 [main] INFO eu.bittrade.libs.steem.api.wrapper.SteemAPIUsageExample - The user dez1337 has done 353 votes so far.
13:05:08.793 [main] INFO eu.bittrade.libs.steem.api.wrapper.SteemAPIUsageExample - His last vote has been done on 2016-12-28T07:31:09.000+0100.
13:05:09.014 [main] INFO eu.bittrade.libs.steem.api.wrapper.SteemAPIUsageExample - dez1337 is one of 122743 accounts on steemit and may increase the number witnesses to 13073 in the near future.
13:05:09.300 [main] INFO eu.bittrade.libs.steem.api.wrapper.SteemAPIUsageExample - The last vote for a article of dez1337 has been done from kevinlp.
13:05:09.422 [main] INFO eu.bittrade.libs.steem.api.wrapper.SteemAPIUsageExample - You may also want to vote for some posts to generate some Steem which is currently worth about 0.152 SBD.
13:05:09.563 [main] ERROR eu.bittrade.libs.steem.api.wrapper.SteemAPIUsageExample - An error of level 13 occured with the following message 13 N5boost16exception_detail10clone_implINS0_19error_info_injectorISt12out_of_rangeEEEE: unknown key
unknown key:
{"name":"thisAcountDoesNo","what":"unknown key"}
th_a database.cpp:322 get_account.
eu.bittrade.libs.steem.api.wrapper.models.error.SteemError@7d0b7e3c[responseId=12,steemErrorDetails=eu.bittrade.libs.steem.api.wrapper.models.error.SteemErrorDetails@2eae8e6e[data=eu.bittrade.libs.steem.api.wrapper.models.error.SteemErrorData@8f2ef19[code=13,name=N5boost16exception_detail10clone_implINS0_19error_info_injectorISt12out_of_rangeEEEE,message=unknown key,stack={eu.bittrade.libs.steem.api.wrapper.models.error.SteemStack@2cf3d63b[context=eu.bittrade.libs.steem.api.wrapper.models.error.SteemContext@7674f035[level=warn,file=database.cpp,line=322,method=get_account,hostname=,threadName=th_a,timestamp=Wed Dec 28 12:05:08 CET 2016],format=${what}: ,data=eu.bittrade.libs.steem.api.wrapper.models.error.SteemData@173ed316[name=thisAcountDoesNo,what=unknown key,type=null,api=null]]}],code=1,message=13 N5boost16exception_detail10clone_implINS0_19error_info_injectorISt12out_of_rangeEEEE: unknown key
unknown key:
{"name":"thisAcountDoesNo","what":"unknown key"}
th_a database.cpp:322 get_account]]

Sort:  

Way to go! Glad you decided to go ahead with it. Congrats! Resteemed.

Thank you again! Your opinion means a lot to me :)

Cool, just wanted to learn Phyton to work with Piston, but an Java-API is much easier to handle for me. Upvoted, Resteemed and Followed.

Ahye, python is also nice espacially if you work on embedded systems like a pi, so you may want to learn either :D It would be quite interesting to me to get some feedback of people who actually use this project, so please keep on and inform me!

What a disorganized and sprawling API !!! It needs to be abstracted into a simpler, easier to use and understand version so it isn't such an obstacle to adoption.

I haven't looked at Xeroc's "Piston" library for python yet, but it is on my list of code to start getting into. I hope it does more than mirror this graphene behemoth and actually refines and abstracts the API.

In my opinion every programmer that takes on the challenge of creating an interface to the graphene API for a different language should also consider doing so in a way that makes using graphene easier for others to learn.

I recognize that is likely an iterative task that takes additional time which may not be necessary to achieve a project's goals or in its schedule to complete, but it's worth thinking about as you create the interface.

Another impediment is simply understanding the plethora of graphene calls in order to abstract them into a higher level of consistency and usefulness. To many the tradeoff of expediency to understand what is necessary to meet a project's requirements and focus only on those API elements may be the difference between reaching the market ahead of competitors or being too late.

First, thank you for the feedback. I think you are basically right in what you are saying, but you have to take care about what you are looking at here. The most projects (steemJs, steem-rpc, steem-cli, and even this project) are simple api wrappers for a specific programming language and nothing more.

No one stops us from creating cool and abstracted services, but all of them will need one of the api wrappers above. Maybe in the future, after this wrapper has been finished, someone will create one of those utility projects that allows an easier access to the steem data.

I'm not sure what your efforts are directed at in providing a wrapper to the API if you haven't got a use for it other than to provide an interface for a specific target language, i.e. you don't have an application in mind.

If that's the case you have the perfect opportunity to do more than just provide a wrapper. You can actually improve access to graphene by defining a new API in the target language which combines similar API calls and objects. Reducing the sheer number of API calls alone would help reduce the API learning curve and improve graphene adoption.

I also see many API calls that are specific to steem. This comment is more directed at cryptonomex (i.e. @dantheman) that you, unless you can easily see the distinctions. This would be another great opportunity for a naming convention to identify app-specific graphene API calls (i.e. BitShares, PeerPlays, Steem, Muse etc). Ideally application specific API calls shouldn't even exist but should be their own dedicated layer on top of the core graphene layer. Note that I said ideally; I recognize that isn't always possible for the sake of efficiency but designs that are designed that way from day 1 are better IMHO.

Another refinement might be to simply group the API calls into related blocks that correspond to the docs (like this basic description of the APIs. You did this in your listing above but it would be even better if the API calls had a prefix or naming convention to make it obvious which API each call was a member of.

Surely you can see similarities of some API methods and data that could be combined or a organized into a better structure. Why have a separate "get_discussions_XXX" API call for each type of XXX rather than a single class with overloaded arguments? Hide all that from the programmer new to the API. Of course each of those methods must exist in the class but I see no reason to bloat the API with such granularity, at least not for a capable object oriented language like Java.

I'm not sure what your efforts are directed at in providing a wrapper to the API if you haven't got a use for it other than to provide an interface for a specific target language, i.e. you don't have an application in mind.

There are some applications that I have in mind, but as I said before: Everything that I or someone else of the community wants to do, needs some kind of this api wrappers. There is no way of creating an application without this.

If that's the case you have the perfect opportunity to do more than just provide a wrapper. You can actually improve access to graphene by defining a new API in the target language which combines similar API calls and objects. Reducing the sheer number of API calls alone would help reduce the API learning curve and improve graphene adoption.

Again you are right, but in my opinion an abstracted, new api is a new project that can be build upon this wrapper and combine its methods. This allows the developer to decide if he want‘s a light weighted api wrapper or a more heavier utility-project which offers him some easy ways to do the things he wants to do. I hadn‘t the deepest look into the piston project, but after the first look it seems to be exactly this: An additional application basend on the steem api wrapper written in python.

I also see many API calls that are specific to steem. This comment is more directed at cryptonomex (i.e. @dantheman) that you, unless you can easily see the distinctions. This would be another great opportunity for a naming convention to identify app-specific graphene API calls (i.e. BitShares, PeerPlays, Steem, Muse etc). Ideally application specific API calls shouldn't even exist but should be their own dedicated layer on top of the core graphene layer. Note that I said ideally; I recognize that isn't always possible for the sake of efficiency but designs that are designed that way from day 1 are better IMHO.

True.

Another refinement might be to simply group the API calls into related blocks that correspond to the docs (like this basic description of the APIs. You did this in your listing above but it would be even better if the API calls had a prefix or naming convention to make it obvious which API each call was a member of.
Surely you can see similarities of some API methods and data that could be combined or a organized into a better structure. Why have a separate "get_discussions_XXX" API call for each type of XXX rather than a single class with overloaded arguments? Hide all that from the programmer new to the API. Of course each of those methods must exist in the class but I see no reason to bloat the API with such granularity, at least not for a capable object oriented language like Java.

There are many good points here! Thank you for that :) - I‘ll keep it in mind.

This post has been ranked within the top 50 most undervalued posts in the second half of Dec 28. We estimate that this post is undervalued by $6.91 as compared to a scenario in which every voter had an equal say.

See the full rankings and details in The Daily Tribune: Dec 28 - Part II. You can also read about some of our methodology, data analysis and technical details in our initial post.

If you are the author and would prefer not to receive these comments, simply reply "Stop" to this comment.

Coin Marketplace

STEEM 0.13
TRX 0.12
JST 0.024
BTC 51460.37
ETH 2243.25
USDT 1.00
SBD 2.01