REST client hints

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
20 messages Options
Reply | Threaded
Open this post in threaded view
|

REST client hints

Ben Coman
I'm just about to write my first client interface to a REST service.
In some respects I understand this is as simple as doing GET responses
using Zinc,
but I'm inquiring about tutorials or libraries that might help.  Most
of the stuff turned up by searches is about server-side of REST.

cheers -ben

Reply | Threaded
Open this post in threaded view
|

Re: REST client hints

EstebanLM
Hi,


On 5 Dec 2017, at 12:15, Ben Coman <[hidden email]> wrote:

I'm just about to write my first client interface to a REST service.
In some respects I understand this is as simple as doing GET responses
using Zinc,

yes, is about that.
GET/POST/PUT/DELETE

all have sense. 

but I'm inquiring about tutorials or libraries that might help.  Most
of the stuff turned up by searches is about server-side of REST.

I’m currently playing with my latest fun project pharo-mastodon, http://github.com/estebanlm/pharo-mastodon (the long term project is “getting out twitter” :P). 
It is actually a rest api… and since is very young, is not complicated at all, so you can take it as an example (probably of "things not to do”, but well… ;) )

Esteban



cheers -ben


Reply | Threaded
Open this post in threaded view
|

Re: REST client hints

Peter Uhnák
With ZnClient you can also dynamically extend paths (#addPath:), specify parameters (#formAt:put:, #queryAt:put:), provide raw #contents:, etc.

But what would be nice is to convert the responses to some domain objects. So e.g. when I query trello for all lists, I would like to get as a result not json, but List instances, where if I asked for cards, it would query the cards from the server.

I recommend also looking at this project by Richard Prinz https://www.min.at/prinz/?x=entry:entry150318-104537 which basically discovers the API and generates a nice implementation for it. Then you use the generated API instead of worrying about constructing HTTP requests.

Peter

On Tue, Dec 5, 2017 at 12:59 PM, Esteban Lorenzano <[hidden email]> wrote:
Hi,


On 5 Dec 2017, at 12:15, Ben Coman <[hidden email]> wrote:

I'm just about to write my first client interface to a REST service.
In some respects I understand this is as simple as doing GET responses
using Zinc,

yes, is about that.
GET/POST/PUT/DELETE

all have sense. 

but I'm inquiring about tutorials or libraries that might help.  Most
of the stuff turned up by searches is about server-side of REST.

I’m currently playing with my latest fun project pharo-mastodon, http://github.com/estebanlm/pharo-mastodon (the long term project is “getting out twitter” :P). 
It is actually a rest api… and since is very young, is not complicated at all, so you can take it as an example (probably of "things not to do”, but well… ;) )

Esteban



cheers -ben



Reply | Threaded
Open this post in threaded view
|

Re: REST client hints

Sven Van Caekenberghe-2
In reply to this post by Ben Coman
Ben,

> On 5 Dec 2017, at 12:15, Ben Coman <[hidden email]> wrote:
>
> I'm just about to write my first client interface to a REST service.
> In some respects I understand this is as simple as doing GET responses
> using Zinc,
> but I'm inquiring about tutorials or libraries that might help.  Most
> of the stuff turned up by searches is about server-side of REST.
>
> cheers -ben

I am not sure what you are looking for, but it is not difficult to do. ZnClient is your go to class.

The Enterprise Pharo book chapters about HTTP client & server are starting points (in particular section 11 of the former, and very specifically 11.3).

HTH,

Sven



Reply | Threaded
Open this post in threaded view
|

Re: REST client hints

Ben Coman
@esteban, thx, I'll take a look at mastodon.

@peter, I remember playing with the Google API when Richard published
that.  thx for the reminder.

@sven, I started reading Enterprise Pharo a couple of hours ago.
I don't quite get your section references. I presume you
don't mean "chapter 11 Persisting Objects with Voyage, 11.3 Enhancing Storage"
That seems off topic. And "chapter 4  Zinc HTTP: The Client Side, 4.11 Headers"
doesn't have a sub-part "3".

I guess part of what I'm interested in are patterns for hooking
NeoJSON up to parse a REST response into objects to build a wrapper
around a REST service. I see a chapter in Enterprise Pharo, which I'll
get to that soon.  Perhaps I was premature asking before reading that,
but its good to have a few paths to explore.

btw, here is the concrete case...
https://bittrex.com/home/api

cheers -ben

On 5 December 2017 at 20:06, Sven Van Caekenberghe <[hidden email]> wrote:

> Ben,
>
>> On 5 Dec 2017, at 12:15, Ben Coman <[hidden email]> wrote:
>>
>> I'm just about to write my first client interface to a REST service.
>> In some respects I understand this is as simple as doing GET responses
>> using Zinc,
>> but I'm inquiring about tutorials or libraries that might help.  Most
>> of the stuff turned up by searches is about server-side of REST.
>>
>> cheers -ben
>
> I am not sure what you are looking for, but it is not difficult to do. ZnClient is your go to class.
>
> The Enterprise Pharo book chapters about HTTP client & server are starting points (in particular section 11 of the former, and very specifically 11.3).
>
> HTH,
>
> Sven
>
>
>

Reply | Threaded
Open this post in threaded view
|

Re: REST client hints

Miguel Moquillon
In reply to this post by Peter Uhnák

There is also the OpenAPI specification dedicated to define a REST API and from which a skeleton of an implementation can be generated. It should be then also possible to generate the skeleton of a client for such an API.

Swagger provides tooling to the OpenAPI specification for several programming languages. Unfortunately, it seams there is no implementation for Smalltalk.

Miguel


Le 05/12/2017 à 13:05, Peter Uhnák a écrit :
With ZnClient you can also dynamically extend paths (#addPath:), specify parameters (#formAt:put:, #queryAt:put:), provide raw #contents:, etc.

But what would be nice is to convert the responses to some domain objects. So e.g. when I query trello for all lists, I would like to get as a result not json, but List instances, where if I asked for cards, it would query the cards from the server.

I recommend also looking at this project by Richard Prinz https://www.min.at/prinz/?x=entry:entry150318-104537 which basically discovers the API and generates a nice implementation for it. Then you use the generated API instead of worrying about constructing HTTP requests.

Peter

On Tue, Dec 5, 2017 at 12:59 PM, Esteban Lorenzano <[hidden email]> wrote:
Hi,


On 5 Dec 2017, at 12:15, Ben Coman <[hidden email]> wrote:

I'm just about to write my first client interface to a REST service.
In some respects I understand this is as simple as doing GET responses
using Zinc,

yes, is about that.
GET/POST/PUT/DELETE

all have sense. 

but I'm inquiring about tutorials or libraries that might help.  Most
of the stuff turned up by searches is about server-side of REST.

I’m currently playing with my latest fun project pharo-mastodon, http://github.com/estebanlm/pharo-mastodon (the long term project is “getting out twitter” :P). 
It is actually a rest api… and since is very young, is not complicated at all, so you can take it as an example (probably of "things not to do”, but well… ;) )

Esteban



cheers -ben




Reply | Threaded
Open this post in threaded view
|

Re: REST client hints

Sven Van Caekenberghe-2
In reply to this post by Ben Coman


> On 5 Dec 2017, at 13:33, Ben Coman <[hidden email]> wrote:
>
> @esteban, thx, I'll take a look at mastodon.
>
> @peter, I remember playing with the Google API when Richard published
> that.  thx for the reminder.
>
> @sven, I started reading Enterprise Pharo a couple of hours ago.
> I don't quite get your section references. I presume you
> don't mean "chapter 11 Persisting Objects with Voyage, 11.3 Enhancing Storage"
> That seems off topic. And "chapter 4  Zinc HTTP: The Client Side, 4.11 Headers"
> doesn't have a sub-part "3".

I meant 11.3 in this page https://ci.inria.fr/pharo-contribution/job/EnterprisePharoBook/lastSuccessfulBuild/artifact/book-result/Zinc-HTTP-Server/Zinc-HTTP-Server.html a section called 11.3. A Zinc Client.

> I guess part of what I'm interested in are patterns for hooking
> NeoJSON up to parse a REST response into objects to build a wrapper
> around a REST service. I see a chapter in Enterprise Pharo, which I'll
> get to that soon.  Perhaps I was premature asking before reading that,
> but its good to have a few paths to explore.

Here is a recent example http://forum.world.st/Another-example-of-invoking-a-REST-JSON-web-service-resolving-User-Agent-strings-tt5017489.html

> btw, here is the concrete case...
> https://bittrex.com/home/api
>
> cheers -ben
>
> On 5 December 2017 at 20:06, Sven Van Caekenberghe <[hidden email]> wrote:
>> Ben,
>>
>>> On 5 Dec 2017, at 12:15, Ben Coman <[hidden email]> wrote:
>>>
>>> I'm just about to write my first client interface to a REST service.
>>> In some respects I understand this is as simple as doing GET responses
>>> using Zinc,
>>> but I'm inquiring about tutorials or libraries that might help.  Most
>>> of the stuff turned up by searches is about server-side of REST.
>>>
>>> cheers -ben
>>
>> I am not sure what you are looking for, but it is not difficult to do. ZnClient is your go to class.
>>
>> The Enterprise Pharo book chapters about HTTP client & server are starting points (in particular section 11 of the former, and very specifically 11.3).
>>
>> HTH,
>>
>> Sven
>>
>>
>>
>


Reply | Threaded
Open this post in threaded view
|

Re: REST client hints

Ben Coman
On 5 December 2017 at 20:44, Sven Van Caekenberghe <[hidden email]> wrote:

>
>
>> On 5 Dec 2017, at 13:33, Ben Coman <[hidden email]> wrote:
>>
>> @esteban, thx, I'll take a look at mastodon.
>>
>> @peter, I remember playing with the Google API when Richard published
>> that.  thx for the reminder.
>>
>> @sven, I started reading Enterprise Pharo a couple of hours ago.
>> I don't quite get your section references. I presume you
>> don't mean "chapter 11 Persisting Objects with Voyage, 11.3 Enhancing Storage"
>> That seems off topic. And "chapter 4  Zinc HTTP: The Client Side, 4.11 Headers"
>> doesn't have a sub-part "3".
>
> I meant 11.3 in this page https://ci.inria.fr/pharo-contribution/job/EnterprisePharoBook/lastSuccessfulBuild/artifact/book-result/Zinc-HTTP-Server/Zinc-HTTP-Server.html a section called 11.3. A Zinc Client.
>
>
>> I guess part of what I'm interested in are patterns for hooking
>> NeoJSON up to parse a REST response into objects to build a wrapper
>> around a REST service. I see a chapter in Enterprise Pharo, which I'll
>> get to that soon.  Perhaps I was premature asking before reading that,
>> but its good to have a few paths to explore.
>
> Here is a recent example http://forum.world.st/Another-example-of-invoking-a-REST-JSON-web-service-resolving-User-Agent-strings-tt5017489.html
>

thx. those are both the examples I was seeking.
btw, note the different numbering here...
http://files.pharo.org/books-pdfs/entreprise-pharo/2016-10-06-EnterprisePharo.pdf
"A Zinc client" is in section 5.11 with part "3" unnumbered

cheers -ben

>> btw, here is the concrete case...
>> https://bittrex.com/home/api
>>
>> cheers -ben
>>
>> On 5 December 2017 at 20:06, Sven Van Caekenberghe <[hidden email]> wrote:
>>> Ben,
>>>
>>>> On 5 Dec 2017, at 12:15, Ben Coman <[hidden email]> wrote:
>>>>
>>>> I'm just about to write my first client interface to a REST service.
>>>> In some respects I understand this is as simple as doing GET responses
>>>> using Zinc,
>>>> but I'm inquiring about tutorials or libraries that might help.  Most
>>>> of the stuff turned up by searches is about server-side of REST.
>>>>
>>>> cheers -ben
>>>
>>> I am not sure what you are looking for, but it is not difficult to do. ZnClient is your go to class.
>>>
>>> The Enterprise Pharo book chapters about HTTP client & server are starting points (in particular section 11 of the former, and very specifically 11.3).
>>>
>>> HTH,
>>>
>>> Sven
>>>
>>>
>>>
>>
>
>

Reply | Threaded
Open this post in threaded view
|

Re: REST client hints

Paul DeBruicker
In reply to this post by Ben Coman
Hi Ben,


I've made a few REST Clients

http://smalltalkhub.com/#!/~pdebruic/Stripe
http://smalltalkhub.com/#!/~pdebruic/Tropo
http://smalltalkhub.com/#!/~pdebruic/SegmentIO

And the elasticsearch one but its been advanced mostly lately by Sho Yoshida
(https://github.com/newapplesho) here
https://github.com/newapplesho/elasticsearch-smalltalk

He has also made REST clients for Twilio, AWS, SendGrid, Salesforce, and
Mixpanel among others.

Norbert Hartl made one for Mandrill (Mailchimps transactional email service)
http://smalltalkhub.com/#!/~NorbertHartl/Mandrill

And Francois Stephany made one for Postmark (another transactional email
service) http://smalltalkhub.com/#!/~PharoExtras/Postmark


So those are some examples of different approaches.  


I also started/made a cross platform web client wrapper (just wraps calls to
ZnClient or WebClient on Squeak)
http://smalltalkhub.com/#!/~pdebruic/HTTPAPIClient

But IIRC I only used it in the Stripe API client.  


Hope this gives you some ideas about how to approach your own solution

Paul



Ben Coman wrote
> I'm just about to write my first client interface to a REST service.
> In some respects I understand this is as simple as doing GET responses
> using Zinc,
> but I'm inquiring about tutorials or libraries that might help.  Most
> of the stuff turned up by searches is about server-side of REST.
>
> cheers -ben





--
Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html

Reply | Threaded
Open this post in threaded view
|

Re: REST client hints

Ben Coman
Thanks Paul. It'll be good to review some concrete implementations,
and nice to see what services other other people find useful. 
I got a momentary urge the create a Pharo Distribution or catalog entry called
something like "SamplRest" with a GUI to explore available REST data sources.
Marketing angle for Pharo would be helping less technical people get a "live" hold of
data from these sources, and then incrementally script against those live objects.
I guess a bit like SegmentIO but on your own desktop rather than through a third party.
(but I've not got the time right now)

cheers -ben

On 6 December 2017 at 02:46, Paul DeBruicker <[hidden email]> wrote:
Hi Ben,


I've made a few REST Clients

http://smalltalkhub.com/#!/~pdebruic/Stripe
http://smalltalkhub.com/#!/~pdebruic/Tropo
http://smalltalkhub.com/#!/~pdebruic/SegmentIO

And the elasticsearch one but its been advanced mostly lately by Sho Yoshida
(https://github.com/newapplesho) here
https://github.com/newapplesho/elasticsearch-smalltalk

He has also made REST clients for Twilio, AWS, SendGrid, Salesforce, and
Mixpanel among others.

Norbert Hartl made one for Mandrill (Mailchimps transactional email service)
http://smalltalkhub.com/#!/~NorbertHartl/Mandrill

And Francois Stephany made one for Postmark (another transactional email
service) http://smalltalkhub.com/#!/~PharoExtras/Postmark


So those are some examples of different approaches.


I also started/made a cross platform web client wrapper (just wraps calls to
ZnClient or WebClient on Squeak)
http://smalltalkhub.com/#!/~pdebruic/HTTPAPIClient

But IIRC I only used it in the Stripe API client.


Hope this gives you some ideas about how to approach your own solution

Paul



Ben Coman wrote
> I'm just about to write my first client interface to a REST service.
> In some respects I understand this is as simple as doing GET responses
> using Zinc,
> but I'm inquiring about tutorials or libraries that might help.  Most
> of the stuff turned up by searches is about server-side of REST.
>
> cheers -ben





--
Sent from: http://forum.world.st/Pharo-Smalltalk-Users-f1310670.html


Reply | Threaded
Open this post in threaded view
|

Re: REST client hints

Ben Coman
In reply to this post by Sven Van Caekenberghe-2

On 5 December 2017 at 20:44, Sven Van Caekenberghe <[hidden email]> wrote:

> On 5 Dec 2017, at 13:33, Ben Coman <[hidden email]> wrote:

> @sven, I started reading Enterprise Pharo a couple of hours ago.
> I don't quite get your section references. I presume you
> don't mean "chapter 11 Persisting Objects with Voyage, 11.3 Enhancing Storage"
> That seems off topic. And "chapter 4  Zinc HTTP: The Client Side, 4.11 Headers"
> doesn't have a sub-part "3".

I meant 11.3 in this page https://ci.inria.fr/pharo-contribution/job/EnterprisePharoBook/lastSuccessfulBuild/artifact/book-result/Zinc-HTTP-Server/Zinc-HTTP-Server.html a section called 11.3. A Zinc Client.

> I guess part of what I'm interested in are patterns for hooking
> NeoJSON up to parse a REST response into objects to build a wrapper
> around a REST service. I see a chapter in Enterprise Pharo, which I'll
> get to that soon.  Perhaps I was premature asking before reading that,
> but its good to have a few paths to explore.

Here is a recent example http://forum.world.st/Another-example-of-invoking-a-REST-JSON-web-service-resolving-User-Agent-strings-tt5017489.html


Thanks Sven.  That helped a lot.   I'd like to report success.
It may be useful to others to see how to progressively build up to parsing a Nested JSON REST


1. First parse the JSON into simple Dictionaries...

(ZnClient new 
enforceHttpSuccess: true;
accept: ZnMimeType applicationJson;
contentReader: [ :entity | NeoJSONReader fromString: entity contents ];
get) inspect.

==>Dictionary( 
      'success' ==> true
      'message' ==> ''
      'result ' ==> an Array(a Dictionary('BaseCurrency'->'BTC' 'BaseCurrencyLong'->'Bitcoin') 
... a Dictionary('BaseCurrency'->'ETH' 'BaseCurrencyLong'->'Ethereum') 



2. Then parse the first level response into a real object...

Object subclass: #BittrexResponse
instanceVariableNames: 'success message result'
classVariableNames: ''
package: 'Bittrex'

(ZnClient new 
enforceHttpSuccess: true;
accept: ZnMimeType applicationJson;
contentReader: [ :entity | 
(NeoJSONReader on: entity readStream)
mapInstVarsFor: BittrexResponse ;
        nextAs: BittrexResponse ];
   get) inspect.

==>BittrexResponse
      success => true
      message => '' 
      result => an Array(a Dictionary('BaseCurrency'->'BTC' 'BaseCurrencyLong'->'Bitcoin') 
... a Dictionary('BaseCurrency'->'ETH' 'BaseCurrencyLong'->'Ethereum') 


Or alternatively...
(ZnClient new 
enforceHttpSuccess: true;
accept: ZnMimeType applicationJson;
contentReader: [ :entity | |reader|
reader := (NeoJSONReader on: entity readStream).
reader for: BittrexResponse do: [:m| 
m mapInstVar: #success.
m mapInstVar: #message.
m mapInstVar: #result ].
      reader nextAs: BittrexResponse ];
   get) inspect.

==>BittrexResponse
      success => true
      message => '' 
      result => an Array(a Dictionary('BaseCurrency'->'BTC' 'BaseCurrencyLong'->'Bitcoin') 
... a Dictionary('BaseCurrency'->'ETH' 'BaseCurrencyLong'->'Ethereum') 


3. Finally parse into real objects the nested level holding the data you really want...

Object subclass: #Market
instanceVariableNames: 'MarketCurrency BaseCurrency MarketCurrencyLong BaseCurrencyLong MinTradeSize MarketName IsActive Created Notice IsSponsored LogoUrl'
classVariableNames: ''
package: 'Bittrex'

(ZnClient new 
enforceHttpSuccess: true;
accept: ZnMimeType applicationJson;
contentReader: [ :entity | |reader|
reader := (NeoJSONReader on: entity readStream).
reader for: BittrexResponse do: [:m| 
m mapInstVar: #success.
m mapInstVar: #message.
(m mapInstVar: #result) valueSchema: #ArrayOfMarkets].
reader for: #ArrayOfMarkets customDo: [ :mapping | mapping listOfElementSchema: Market ].
reader mapInstVarsFor: Market. 
      reader nextAs: BittrexResponse ];
   get) inspect.

==>BittrexResponse
      success => true
      message => '' 
      result => an Array(a Market(LTC) a Market(DOGE) a Market(VTC) a Market(PPC) a Market(FTC) a Market(RDD) 
... Market(POWR) a Market(BTG) a Market(BTG) a Market(BTG) a Market(ADA) a Market(ENG) a Market(ENG))


WhooHoo!


A couple of things remaining:

* The instance variables of Market currently start with an upper-case to match the JSON fields.
  Lower-casing the first letter breaks things.  
  What strategies can be used to conform here to Smalltalk conventions?
  Or is it easy enough to live with it?

* In various posts I've seen mention of a class-side method #neoJsonMapping:
   but there is no explanation of this in the Enterprise Book.  
   How might #neoJsonMapping: come into the picture for my use case above?

cheers -ben

Reply | Threaded
Open this post in threaded view
|

Re: REST client hints

Stephane Ducasse-3
Tx for your report :)

https://wordpress.com/post/pharoweekly.wordpress.com/2347

Did you look at SmartShackles? on our inria github repo. Because
Santiago is extracting information from blockchains.

On Sun, Dec 10, 2017 at 4:45 AM, Ben Coman <[hidden email]> wrote:

>
> On 5 December 2017 at 20:44, Sven Van Caekenberghe <[hidden email]> wrote:
>>
>>
>> > On 5 Dec 2017, at 13:33, Ben Coman <[hidden email]> wrote:
>>
>> > @sven, I started reading Enterprise Pharo a couple of hours ago.
>> > I don't quite get your section references. I presume you
>> > don't mean "chapter 11 Persisting Objects with Voyage, 11.3 Enhancing
>> > Storage"
>> > That seems off topic. And "chapter 4  Zinc HTTP: The Client Side, 4.11
>> > Headers"
>> > doesn't have a sub-part "3".
>>
>> I meant 11.3 in this page
>> https://ci.inria.fr/pharo-contribution/job/EnterprisePharoBook/lastSuccessfulBuild/artifact/book-result/Zinc-HTTP-Server/Zinc-HTTP-Server.html
>> a section called 11.3. A Zinc Client.
>>
>> > I guess part of what I'm interested in are patterns for hooking
>> > NeoJSON up to parse a REST response into objects to build a wrapper
>> > around a REST service. I see a chapter in Enterprise Pharo, which I'll
>> > get to that soon.  Perhaps I was premature asking before reading that,
>> > but its good to have a few paths to explore.
>>
>> Here is a recent example
>> http://forum.world.st/Another-example-of-invoking-a-REST-JSON-web-service-resolving-User-Agent-strings-tt5017489.html
>
>
>
> Thanks Sven.  That helped a lot.   I'd like to report success.
> It may be useful to others to see how to progressively build up to parsing a
> Nested JSON REST
>
>
> 1. First parse the JSON into simple Dictionaries...
>
> (ZnClient new
> url: 'https://bittrex.com/api/v1.1/public/getmarkets';
> enforceHttpSuccess: true;
> accept: ZnMimeType applicationJson;
> contentReader: [ :entity | NeoJSONReader fromString: entity contents ];
> get) inspect.
>
> ==>Dictionary(
>       'success' ==> true
>       'message' ==> ''
>       'result ' ==> an Array(a Dictionary('BaseCurrency'->'BTC'
> 'BaseCurrencyLong'->'Bitcoin')
> ... a Dictionary('BaseCurrency'->'ETH' 'BaseCurrencyLong'->'Ethereum')
>
>
>
> 2. Then parse the first level response into a real object...
>
> Object subclass: #BittrexResponse
> instanceVariableNames: 'success message result'
> classVariableNames: ''
> package: 'Bittrex'
>
> (ZnClient new
> url: 'https://bittrex.com/api/v1.1/public/getmarkets';
> enforceHttpSuccess: true;
> accept: ZnMimeType applicationJson;
> contentReader: [ :entity |
> (NeoJSONReader on: entity readStream)
> mapInstVarsFor: BittrexResponse ;
>         nextAs: BittrexResponse ];
>    get) inspect.
>
> ==>BittrexResponse
>       success => true
>       message => ''
>       result => an Array(a Dictionary('BaseCurrency'->'BTC'
> 'BaseCurrencyLong'->'Bitcoin')
> ... a Dictionary('BaseCurrency'->'ETH' 'BaseCurrencyLong'->'Ethereum')
>
>
> Or alternatively...
> (ZnClient new
> url: 'https://bittrex.com/api/v1.1/public/getmarkets';
> enforceHttpSuccess: true;
> accept: ZnMimeType applicationJson;
> contentReader: [ :entity | |reader|
> reader := (NeoJSONReader on: entity readStream).
> reader for: BittrexResponse do: [:m|
> m mapInstVar: #success.
> m mapInstVar: #message.
> m mapInstVar: #result ].
>       reader nextAs: BittrexResponse ];
>    get) inspect.
>
> ==>BittrexResponse
>       success => true
>       message => ''
>       result => an Array(a Dictionary('BaseCurrency'->'BTC'
> 'BaseCurrencyLong'->'Bitcoin')
> ... a Dictionary('BaseCurrency'->'ETH' 'BaseCurrencyLong'->'Ethereum')
>
>
> 3. Finally parse into real objects the nested level holding the data you
> really want...
>
> Object subclass: #Market
> instanceVariableNames: 'MarketCurrency BaseCurrency MarketCurrencyLong
> BaseCurrencyLong MinTradeSize MarketName IsActive Created Notice IsSponsored
> LogoUrl'
> classVariableNames: ''
> package: 'Bittrex'
>
> (ZnClient new
> url: 'https://bittrex.com/api/v1.1/public/getmarkets';
> enforceHttpSuccess: true;
> accept: ZnMimeType applicationJson;
> contentReader: [ :entity | |reader|
> reader := (NeoJSONReader on: entity readStream).
> reader for: BittrexResponse do: [:m|
> m mapInstVar: #success.
> m mapInstVar: #message.
> (m mapInstVar: #result) valueSchema: #ArrayOfMarkets].
> reader for: #ArrayOfMarkets customDo: [ :mapping | mapping
> listOfElementSchema: Market ].
> reader mapInstVarsFor: Market.
>       reader nextAs: BittrexResponse ];
>    get) inspect.
>
> ==>BittrexResponse
>       success => true
>       message => ''
>       result => an Array(a Market(LTC) a Market(DOGE) a Market(VTC) a
> Market(PPC) a Market(FTC) a Market(RDD)
> ... Market(POWR) a Market(BTG) a Market(BTG) a Market(BTG) a Market(ADA) a
> Market(ENG) a Market(ENG))
>
>
> WhooHoo!
>
>
> A couple of things remaining:
>
> * The instance variables of Market currently start with an upper-case to
> match the JSON fields.
>   Lower-casing the first letter breaks things.
>   What strategies can be used to conform here to Smalltalk conventions?
>   Or is it easy enough to live with it?
>
> * In various posts I've seen mention of a class-side method #neoJsonMapping:
>    but there is no explanation of this in the Enterprise Book.
>    How might #neoJsonMapping: come into the picture for my use case above?
>
> cheers -ben
>

Reply | Threaded
Open this post in threaded view
|

Re: REST client hints

Ben Coman


On 10 December 2017 at 15:27, Stephane Ducasse <[hidden email]> wrote:
Tx for your report :)

https://wordpress.com/post/pharoweekly.wordpress.com/2347

Did you look at SmartShackles? on our inria github repo. Because
Santiago is extracting information from blockchains.


I would hesitate to call where I'm up to right now as blockchain related.  
Its just the trading api of one exchange where the items traded happen to be bitcoins.
But I have a growing interest in blockchains, so thanks for the tip...

I didn't see anything relevant here...  https://github.com/INRIA
but with that product name I found... https://github.com/sbragagnolo/SmartShackle

cheers -ben
Reply | Threaded
Open this post in threaded view
|

Re: REST client hints

Sven Van Caekenberghe-2
In reply to this post by Ben Coman


> On 10 Dec 2017, at 04:45, Ben Coman <[hidden email]> wrote:
>
>
> On 5 December 2017 at 20:44, Sven Van Caekenberghe <[hidden email]> wrote:
>
> > On 5 Dec 2017, at 13:33, Ben Coman <[hidden email]> wrote:
>
> > @sven, I started reading Enterprise Pharo a couple of hours ago.
> > I don't quite get your section references. I presume you
> > don't mean "chapter 11 Persisting Objects with Voyage, 11.3 Enhancing Storage"
> > That seems off topic. And "chapter 4  Zinc HTTP: The Client Side, 4.11 Headers"
> > doesn't have a sub-part "3".
>
> I meant 11.3 in this page https://ci.inria.fr/pharo-contribution/job/EnterprisePharoBook/lastSuccessfulBuild/artifact/book-result/Zinc-HTTP-Server/Zinc-HTTP-Server.html a section called 11.3. A Zinc Client.
>
> > I guess part of what I'm interested in are patterns for hooking
> > NeoJSON up to parse a REST response into objects to build a wrapper
> > around a REST service. I see a chapter in Enterprise Pharo, which I'll
> > get to that soon.  Perhaps I was premature asking before reading that,
> > but its good to have a few paths to explore.
>
> Here is a recent example http://forum.world.st/Another-example-of-invoking-a-REST-JSON-web-service-resolving-User-Agent-strings-tt5017489.html
>
>
> Thanks Sven.  That helped a lot.   I'd like to report success.
> It may be useful to others to see how to progressively build up to parsing a Nested JSON REST
>
>
> 1. First parse the JSON into simple Dictionaries...
>
> (ZnClient new
> url: 'https://bittrex.com/api/v1.1/public/getmarkets';
> enforceHttpSuccess: true;
> accept: ZnMimeType applicationJson;
> contentReader: [ :entity | NeoJSONReader fromString: entity contents ];
> get) inspect.
>
> ==>Dictionary(
>       'success' ==> true
>       'message' ==> ''
>       'result ' ==> an Array(a Dictionary('BaseCurrency'->'BTC' 'BaseCurrencyLong'->'Bitcoin')
> ... a Dictionary('BaseCurrency'->'ETH' 'BaseCurrencyLong'->'Ethereum')
>
>
>
> 2. Then parse the first level response into a real object...
>
> Object subclass: #BittrexResponse
> instanceVariableNames: 'success message result'
> classVariableNames: ''
> package: 'Bittrex'
>
> (ZnClient new
> url: 'https://bittrex.com/api/v1.1/public/getmarkets';
> enforceHttpSuccess: true;
> accept: ZnMimeType applicationJson;
> contentReader: [ :entity |
> (NeoJSONReader on: entity readStream)
> mapInstVarsFor: BittrexResponse ;
>         nextAs: BittrexResponse ];
>    get) inspect.
>
> ==>BittrexResponse
>       success => true
>       message => ''
>       result => an Array(a Dictionary('BaseCurrency'->'BTC' 'BaseCurrencyLong'->'Bitcoin')
> ... a Dictionary('BaseCurrency'->'ETH' 'BaseCurrencyLong'->'Ethereum')
>
>
> Or alternatively...
> (ZnClient new
> url: 'https://bittrex.com/api/v1.1/public/getmarkets';
> enforceHttpSuccess: true;
> accept: ZnMimeType applicationJson;
> contentReader: [ :entity | |reader|
> reader := (NeoJSONReader on: entity readStream).
> reader for: BittrexResponse do: [:m|
> m mapInstVar: #success.
> m mapInstVar: #message.
> m mapInstVar: #result ].
>       reader nextAs: BittrexResponse ];
>    get) inspect.
>
> ==>BittrexResponse
>       success => true
>       message => ''
>       result => an Array(a Dictionary('BaseCurrency'->'BTC' 'BaseCurrencyLong'->'Bitcoin')
> ... a Dictionary('BaseCurrency'->'ETH' 'BaseCurrencyLong'->'Ethereum')
>
>
> 3. Finally parse into real objects the nested level holding the data you really want...
>
> Object subclass: #Market
> instanceVariableNames: 'MarketCurrency BaseCurrency MarketCurrencyLong BaseCurrencyLong MinTradeSize MarketName IsActive Created Notice IsSponsored LogoUrl'
> classVariableNames: ''
> package: 'Bittrex'
>
> (ZnClient new
> url: 'https://bittrex.com/api/v1.1/public/getmarkets';
> enforceHttpSuccess: true;
> accept: ZnMimeType applicationJson;
> contentReader: [ :entity | |reader|
> reader := (NeoJSONReader on: entity readStream).
> reader for: BittrexResponse do: [:m|
> m mapInstVar: #success.
> m mapInstVar: #message.
> (m mapInstVar: #result) valueSchema: #ArrayOfMarkets].
> reader for: #ArrayOfMarkets customDo: [ :mapping | mapping listOfElementSchema: Market ].
> reader mapInstVarsFor: Market.
>       reader nextAs: BittrexResponse ];
>    get) inspect.
>
> ==>BittrexResponse
>       success => true
>       message => ''
>       result => an Array(a Market(LTC) a Market(DOGE) a Market(VTC) a Market(PPC) a Market(FTC) a Market(RDD)
> ... Market(POWR) a Market(BTG) a Market(BTG) a Market(BTG) a Market(ADA) a Market(ENG) a Market(ENG))
>
>
> WhooHoo!

Great, I'm glad you're happy.

> A couple of things remaining:
>
> * The instance variables of Market currently start with an upper-case to match the JSON fields.
>   Lower-casing the first letter breaks things.  
>   What strategies can be used to conform here to Smalltalk conventions?
>   Or is it easy enough to live with it?

You can map properties (the key/values in JSON maps) using different mechanisms. Directly via instance variables, via accessors and via blocks. In the first two cases, the first approach is indeed to match the names literally. But there are also the variants #mapInstVar:to: and #mapAccessor:to: to have a different name.

  mapping mapInstVar: #markerCurrency to: #MarketCurrency

> * In various posts I've seen mention of a class-side method #neoJsonMapping:
>    but there is no explanation of this in the Enterprise Book.  
>    How might #neoJsonMapping: come into the picture for my use case above?

A mapper (reader or writer) looks for mappings in itself (like how you used it) but also on the class side of schema/class names. So instead of doing the mapping in the construction of the reader/writer you can write a fixed on the class side. Look for implementors of #neoJsonMapping:

The difference is mainly that scope: per reader/writer allows for different mappings depending on use case, while the class side #neoJsonMapping: is global. Some people like a schema definition in one place, other like a distributed one (with inheritance options).

Sven

> cheers -ben
>


Reply | Threaded
Open this post in threaded view
|

Re: REST client hints

Ben Coman
In reply to this post by Ben Coman
Hi Sven (et al),

On 10 December 2017 at 11:45, Ben Coman <[hidden email]> wrote:

3. Finally parse into real objects the nested level holding the data you really want...

Object subclass: #Market
instanceVariableNames: 'MarketCurrency BaseCurrency MarketCurrencyLong BaseCurrencyLong MinTradeSize MarketName IsActive Created Notice IsSponsored LogoUrl'
classVariableNames: ''
package: 'Bittrex'

(ZnClient new 
enforceHttpSuccess: true;
accept: ZnMimeType applicationJson;
contentReader: [ :entity | |reader|
reader := (NeoJSONReader on: entity readStream).
reader for: BittrexResponse do: [:m| 
m mapInstVar: #success.
m mapInstVar: #message.
(m mapInstVar: #result) valueSchema: #ArrayOfMarkets].
reader for: #ArrayOfMarkets customDo: [ :mapping | mapping listOfElementSchema: Market ].
reader mapInstVarsFor: Market. 
      reader nextAs: BittrexResponse ];
   get) inspect.

==>BittrexResponse
      success => true
      message => '' 
      result => an Array(a Market(LTC) a Market(DOGE) a Market(VTC) a Market(PPC) a Market(FTC) a Market(RDD) 
... Market(POWR) a Market(BTG) a Market(BTG) a Market(BTG) a Market(ADA) a Market(ENG) a Market(ENG))

So the code of [3.] above works fine when the raw response looks like this...
(ZnClient new 
get) inspect.
==> {"success":true,
          "message":"",
          "result": [{"MarketName":"BTC-LTC},{"MarketName":"BTC-NXT}]
        }

But of course [3.] doesn't work when the raw response looks like this...
==> {"success":true,
           "message":"",
           "result":{"Bid":0.01765465,"Ask":0.01769000,"Last":0.01763350}
       }

since result is a json-object not a json-array.  But I can't work out 
how to map the JSON object into the 'result' instance variable of BittrexResponse, 
where...

BittrexObject subclass: #BittrexTicker
instanceVariableNames: 'Bid Ask Last'
classVariableNames: ''
package: 'Bittrex'


My best guesses so far are uncommenting either [A.] or [B.] below...

(ZnClient new 
enforceHttpSuccess: true;
accept: ZnMimeType applicationJson;
contentReader: [ :entity | |reader|
reader := (NeoJSONReader on: entity readStream).
reader for: BittrexResponse do: [:m| 
m mapInstVar: #success.
m mapInstVar: #message.
(m mapInstVar: #result) valueSchema: #BittrexTicker].
"A. reader for: BittrexTicker do: [ :m|
m mapInstVar: #Bid]."
"B. reader for: #BittrexTicker customDo: [ :mapping |
mapping mapWithValueSchema: BittrexTicker]."
      reader nextAs: BittrexResponse ];
   get).


Can you advise what formulation is required? 
cheers -ben
Reply | Threaded
Open this post in threaded view
|

Re: REST client hints

Sven Van Caekenberghe-2
Yeah, that is the main problem with JSON. It cannot capture that variability in its own spec. Sure, there are tons of extensions on top of JSON to address these aspects, but NeoJSON only deals with the raw spec. And any server can use its own version.

Simple answer is indeed: getmarkets and getticker return different schema, since you know what you call, you can map accordingly. (And hope the server does not do further variability). There cannot be one global mapping.

> On 13 Dec 2017, at 05:37, Ben Coman <[hidden email]> wrote:
>
> Hi Sven (et al),
>
> On 10 December 2017 at 11:45, Ben Coman <[hidden email]> wrote:
>
> 3. Finally parse into real objects the nested level holding the data you really want...
>
> Object subclass: #Market
> instanceVariableNames: 'MarketCurrency BaseCurrency MarketCurrencyLong BaseCurrencyLong MinTradeSize MarketName IsActive Created Notice IsSponsored LogoUrl'
> classVariableNames: ''
> package: 'Bittrex'
>
> (ZnClient new
> url: 'https://bittrex.com/api/v1.1/public/getmarkets';
> enforceHttpSuccess: true;
> accept: ZnMimeType applicationJson;
> contentReader: [ :entity | |reader|
> reader := (NeoJSONReader on: entity readStream).
> reader for: BittrexResponse do: [:m|
> m mapInstVar: #success.
> m mapInstVar: #message.
> (m mapInstVar: #result) valueSchema: #ArrayOfMarkets].
> reader for: #ArrayOfMarkets customDo: [ :mapping | mapping listOfElementSchema: Market ].
> reader mapInstVarsFor: Market.
>       reader nextAs: BittrexResponse ];
>    get) inspect.
>
> ==>BittrexResponse
>       success => true
>       message => ''
>       result => an Array(a Market(LTC) a Market(DOGE) a Market(VTC) a Market(PPC) a Market(FTC) a Market(RDD)
> ... Market(POWR) a Market(BTG) a Market(BTG) a Market(BTG) a Market(ADA) a Market(ENG) a Market(ENG))
>
> So the code of [3.] above works fine when the raw response looks like this...
> (ZnClient new
> url: 'https://bittrex.com/api/v1.1/public/getmarkets';
> get) inspect.
> ==> {"success":true,
>           "message":"",
>           "result": [{"MarketName":"BTC-LTC},{"MarketName":"BTC-NXT}]
>         }
>
> But of course [3.] doesn't work when the raw response looks like this...
> (ZnClient new
> url: 'https://bittrex.com/api/v1.1/public/getticker?market=BTC-LTC';
> get) inspect.
> ==> {"success":true,
>            "message":"",
>            "result":{"Bid":0.01765465,"Ask":0.01769000,"Last":0.01763350}
>        }
>
> since result is a json-object not a json-array.  But I can't work out
> how to map the JSON object into the 'result' instance variable of BittrexResponse,
> where...
>
> BittrexObject subclass: #BittrexTicker
> instanceVariableNames: 'Bid Ask Last'
> classVariableNames: ''
> package: 'Bittrex'
>
>
> My best guesses so far are uncommenting either [A.] or [B.] below...
>
> (ZnClient new
> url: 'https://bittrex.com/api/v1.1/public/getticker?market=BTC-LTC';
> enforceHttpSuccess: true;
> accept: ZnMimeType applicationJson;
> contentReader: [ :entity | |reader|
> reader := (NeoJSONReader on: entity readStream).
> reader for: BittrexResponse do: [:m|
> m mapInstVar: #success.
> m mapInstVar: #message.
> (m mapInstVar: #result) valueSchema: #BittrexTicker].
> "A. reader for: BittrexTicker do: [ :m|
> m mapInstVar: #Bid]."
> "B. reader for: #BittrexTicker customDo: [ :mapping |
> mapping mapWithValueSchema: BittrexTicker]."
>       reader nextAs: BittrexResponse ];
>    get).
>
>
> Can you advise what formulation is required?
> cheers -ben


Reply | Threaded
Open this post in threaded view
|

Re: REST client hints

Ben Coman


On 13 December 2017 at 14:40, Sven Van Caekenberghe <[hidden email]> wrote:
Yeah, that is the main problem with JSON. It cannot capture that variability in its own spec. Sure, there are tons of extensions on top of JSON to address these aspects, but NeoJSON only deals with the raw spec. And any server can use its own version.

Simple answer is indeed: getmarkets and getticker return different schema, since you know what you call, you can map accordingly. (And hope the server does not do further variability). There cannot be one global mapping.


Thats cool.  I'm not looking for one global mapping to simultaneously handle getmarkets and getticker together.
I'm just looking to handle getticker in isolation.  I can't work that part out.

cheers -ben
 

> On 13 Dec 2017, at 05:37, Ben Coman <[hidden email]> wrote:
>
> Hi Sven (et al),
>
> On 10 December 2017 at 11:45, Ben Coman <[hidden email]> wrote:
>
> 3. Finally parse into real objects the nested level holding the data you really want...
>
> Object subclass: #Market
>       instanceVariableNames: 'MarketCurrency BaseCurrency MarketCurrencyLong BaseCurrencyLong MinTradeSize MarketName IsActive Created Notice IsSponsored LogoUrl'
>       classVariableNames: ''
>       package: 'Bittrex'
>
> (ZnClient new
>       url: 'https://bittrex.com/api/v1.1/public/getmarkets';
>       enforceHttpSuccess: true;
>       accept: ZnMimeType applicationJson;
>       contentReader: [ :entity | |reader|
>               reader := (NeoJSONReader on: entity readStream).
>               reader for: BittrexResponse do: [:m|
>                       m mapInstVar: #success.
>                       m mapInstVar: #message.
>                       (m mapInstVar: #result) valueSchema: #ArrayOfMarkets].
>               reader for: #ArrayOfMarkets customDo: [ :mapping | mapping listOfElementSchema: Market ].
>               reader mapInstVarsFor: Market.
>       reader nextAs: BittrexResponse ];
>    get) inspect.
>
> ==>BittrexResponse
>       success => true
>       message => ''
>       result => an Array(a Market(LTC) a Market(DOGE) a Market(VTC) a Market(PPC) a Market(FTC) a Market(RDD)
> ... Market(POWR) a Market(BTG) a Market(BTG) a Market(BTG) a Market(ADA) a Market(ENG) a Market(ENG))
>
> So the code of [3.] above works fine when the raw response looks like this...
> (ZnClient new
>       url: 'https://bittrex.com/api/v1.1/public/getmarkets';
>       get) inspect.
> ==> {"success":true,
>           "message":"",
>           "result": [{"MarketName":"BTC-LTC},{"MarketName":"BTC-NXT}]
>         }
>
> But of course [3.] doesn't work when the raw response looks like this...
> (ZnClient new
>       url: 'https://bittrex.com/api/v1.1/public/getticker?market=BTC-LTC';
>       get) inspect.
> ==> {"success":true,
>            "message":"",
>            "result":{"Bid":0.01765465,"Ask":0.01769000,"Last":0.01763350}
>        }
>
> since result is a json-object not a json-array.  But I can't work out
> how to map the JSON object into the 'result' instance variable of BittrexResponse,
> where...
>
> BittrexObject subclass: #BittrexTicker
>       instanceVariableNames: 'Bid Ask Last'
>       classVariableNames: ''
>       package: 'Bittrex'
>
>
> My best guesses so far are uncommenting either [A.] or [B.] below...
>
> (ZnClient new
>       url: 'https://bittrex.com/api/v1.1/public/getticker?market=BTC-LTC';
>       enforceHttpSuccess: true;
>       accept: ZnMimeType applicationJson;
>       contentReader: [ :entity | |reader|
>               reader := (NeoJSONReader on: entity readStream).
>               reader for: BittrexResponse do: [:m|
>                       m mapInstVar: #success.
>                       m mapInstVar: #message.
>                       (m mapInstVar: #result) valueSchema: #BittrexTicker].
> "A.           reader for: BittrexTicker do: [ :m|
>                        m mapInstVar: #Bid]."
> "B.           reader for: #BittrexTicker customDo: [ :mapping |
>                       mapping mapWithValueSchema: BittrexTicker]."
>       reader nextAs: BittrexResponse ];
>    get).
>
>
> Can you advise what formulation is required?
> cheers -ben



Reply | Threaded
Open this post in threaded view
|

Re: REST client hints

Sven Van Caekenberghe-2


> On 13 Dec 2017, at 07:49, Ben Coman <[hidden email]> wrote:
>
>
>
> On 13 December 2017 at 14:40, Sven Van Caekenberghe <[hidden email]> wrote:
> Yeah, that is the main problem with JSON. It cannot capture that variability in its own spec. Sure, there are tons of extensions on top of JSON to address these aspects, but NeoJSON only deals with the raw spec. And any server can use its own version.
>
> Simple answer is indeed: getmarkets and getticker return different schema, since you know what you call, you can map accordingly. (And hope the server does not do further variability). There cannot be one global mapping.
>
>
> Thats cool.  I'm not looking for one global mapping to simultaneously handle getmarkets and getticker together.
> I'm just looking to handle getticker in isolation.  I can't work that part out.

Hmm, your result ivar in response is of type ticker which maps its 3 ivars. So uncomment A but add all 3 ivars to ticker. no ?

> cheers -ben
>  
>
> > On 13 Dec 2017, at 05:37, Ben Coman <[hidden email]> wrote:
> >
> > Hi Sven (et al),
> >
> > On 10 December 2017 at 11:45, Ben Coman <[hidden email]> wrote:
> >
> > 3. Finally parse into real objects the nested level holding the data you really want...
> >
> > Object subclass: #Market
> >       instanceVariableNames: 'MarketCurrency BaseCurrency MarketCurrencyLong BaseCurrencyLong MinTradeSize MarketName IsActive Created Notice IsSponsored LogoUrl'
> >       classVariableNames: ''
> >       package: 'Bittrex'
> >
> > (ZnClient new
> >       url: 'https://bittrex.com/api/v1.1/public/getmarkets';
> >       enforceHttpSuccess: true;
> >       accept: ZnMimeType applicationJson;
> >       contentReader: [ :entity | |reader|
> >               reader := (NeoJSONReader on: entity readStream).
> >               reader for: BittrexResponse do: [:m|
> >                       m mapInstVar: #success.
> >                       m mapInstVar: #message.
> >                       (m mapInstVar: #result) valueSchema: #ArrayOfMarkets].
> >               reader for: #ArrayOfMarkets customDo: [ :mapping | mapping listOfElementSchema: Market ].
> >               reader mapInstVarsFor: Market.
> >       reader nextAs: BittrexResponse ];
> >    get) inspect.
> >
> > ==>BittrexResponse
> >       success => true
> >       message => ''
> >       result => an Array(a Market(LTC) a Market(DOGE) a Market(VTC) a Market(PPC) a Market(FTC) a Market(RDD)
> > ... Market(POWR) a Market(BTG) a Market(BTG) a Market(BTG) a Market(ADA) a Market(ENG) a Market(ENG))
> >
> > So the code of [3.] above works fine when the raw response looks like this...
> > (ZnClient new
> >       url: 'https://bittrex.com/api/v1.1/public/getmarkets';
> >       get) inspect.
> > ==> {"success":true,
> >           "message":"",
> >           "result": [{"MarketName":"BTC-LTC},{"MarketName":"BTC-NXT}]
> >         }
> >
> > But of course [3.] doesn't work when the raw response looks like this...
> > (ZnClient new
> >       url: 'https://bittrex.com/api/v1.1/public/getticker?market=BTC-LTC';
> >       get) inspect.
> > ==> {"success":true,
> >            "message":"",
> >            "result":{"Bid":0.01765465,"Ask":0.01769000,"Last":0.01763350}
> >        }
> >
> > since result is a json-object not a json-array.  But I can't work out
> > how to map the JSON object into the 'result' instance variable of BittrexResponse,
> > where...
> >
> > BittrexObject subclass: #BittrexTicker
> >       instanceVariableNames: 'Bid Ask Last'
> >       classVariableNames: ''
> >       package: 'Bittrex'
> >
> >
> > My best guesses so far are uncommenting either [A.] or [B.] below...
> >
> > (ZnClient new
> >       url: 'https://bittrex.com/api/v1.1/public/getticker?market=BTC-LTC';
> >       enforceHttpSuccess: true;
> >       accept: ZnMimeType applicationJson;
> >       contentReader: [ :entity | |reader|
> >               reader := (NeoJSONReader on: entity readStream).
> >               reader for: BittrexResponse do: [:m|
> >                       m mapInstVar: #success.
> >                       m mapInstVar: #message.
> >                       (m mapInstVar: #result) valueSchema: #BittrexTicker].
> > "A.           reader for: BittrexTicker do: [ :m|
> >                        m mapInstVar: #Bid]."
> > "B.           reader for: #BittrexTicker customDo: [ :mapping |
> >                       mapping mapWithValueSchema: BittrexTicker]."
> >       reader nextAs: BittrexResponse ];
> >    get).
> >
> >
> > Can you advise what formulation is required?
> > cheers -ben
>
>
>


Reply | Threaded
Open this post in threaded view
|

Re: REST client hints

Ben Coman

> > On 13 Dec 2017, at 05:37, Ben Coman <[hidden email]> wrote:
> >
> > Hi Sven (et al),
> >
> > On 10 December 2017 at 11:45, Ben Coman <[hidden email]> wrote:
> >
> > 3. Finally parse into real objects the nested level holding the data you really want...
> >
> > Object subclass: #Market
> >       instanceVariableNames: 'MarketCurrency BaseCurrency MarketCurrencyLong BaseCurrencyLong MinTradeSize MarketName IsActive Created Notice IsSponsored LogoUrl'
> >       classVariableNames: ''
> >       package: 'Bittrex'
> >
> > (ZnClient new
> >       url: 'https://bittrex.com/api/v1.1/public/getmarkets';
> >       enforceHttpSuccess: true;
> >       accept: ZnMimeType applicationJson;
> >       contentReader: [ :entity | |reader|
> >               reader := (NeoJSONReader on: entity readStream).
> >               reader for: BittrexResponse do: [:m|
> >                       m mapInstVar: #success.
> >                       m mapInstVar: #message.
> >                       (m mapInstVar: #result) valueSchema: #ArrayOfMarkets].
> >               reader for: #ArrayOfMarkets customDo: [ :mapping | mapping listOfElementSchema: Market ].
> >               reader mapInstVarsFor: Market.
> >       reader nextAs: BittrexResponse ];
> >    get) inspect.
> >
> > ==>BittrexResponse
> >       success => true
> >       message => ''
> >       result => an Array(a Market(LTC) a Market(DOGE) a Market(VTC) a Market(PPC) a Market(FTC) a Market(RDD)
> > ... Market(POWR) a Market(BTG) a Market(BTG) a Market(BTG) a Market(ADA) a Market(ENG) a Market(ENG))
> >
> > So the code of [3.] above works fine when the raw response looks like this...
> > (ZnClient new
> >       url: 'https://bittrex.com/api/v1.1/public/getmarkets';
> >       get) inspect.
> > ==> {"success":true,
> >           "message":"",
> >           "result": [{"MarketName":"BTC-LTC},{"MarketName":"BTC-NXT}]
> >         }
> >
> > But of course [3.] doesn't work when the raw response looks like this...
> > (ZnClient new
> >       url: 'https://bittrex.com/api/v1.1/public/getticker?market=BTC-LTC';
> >       get) inspect.
> > ==> {"success":true,
> >            "message":"",
> >            "result":{"Bid":0.01765465,"Ask":0.01769000,"Last":0.01763350}
> >        }
> >
> > since result is a json-object not a json-array.  But I can't work out
> > how to map the JSON object into the 'result' instance variable of BittrexResponse,
> > where...
> >
> > BittrexObject subclass: #BittrexTicker
> >       instanceVariableNames: 'Bid Ask Last'
> >       classVariableNames: ''
> >       package: 'Bittrex'
> >
> >
> > My best guesses so far are uncommenting either [A.] or [B.] below...
> >
> > (ZnClient new
> >       url: 'https://bittrex.com/api/v1.1/public/getticker?market=BTC-LTC';
> >       enforceHttpSuccess: true;
> >       accept: ZnMimeType applicationJson;
> >       contentReader: [ :entity | |reader|
> >               reader := (NeoJSONReader on: entity readStream).
> >               reader for: BittrexResponse do: [:m|
> >                       m mapInstVar: #success.
> >                       m mapInstVar: #message.
> >                       (m mapInstVar: #result) valueSchema: #BittrexTicker].
> > "A.           reader for: BittrexTicker do: [ :m|
> >                        m mapInstVar: #Bid]."
> > "B.           reader for: #BittrexTicker customDo: [ :mapping |
> >                       mapping mapWithValueSchema: BittrexTicker]."
> >       reader nextAs: BittrexResponse ];
> >    get).
> >
> >
> > Can you advise what formulation is required?

 
On 13 December 2017 at 15:33, Sven Van Caekenberghe <[hidden email]> wrote:


> On 13 Dec 2017, at 07:49, Ben Coman <[hidden email]> wrote:
>
>
>
> On 13 December 2017 at 14:40, Sven Van Caekenberghe <[hidden email]> wrote:
> Yeah, that is the main problem with JSON. It cannot capture that variability in its own spec. Sure, there are tons of extensions on top of JSON to address these aspects, but NeoJSON only deals with the raw spec. And any server can use its own version.
>
> Simple answer is indeed: getmarkets and getticker return different schema, since you know what you call, you can map accordingly. (And hope the server does not do further variability). There cannot be one global mapping.
>
>
> Thats cool.  I'm not looking for one global mapping to simultaneously handle getmarkets and getticker together.
> I'm just looking to handle getticker in isolation.  I can't work that part out.

Hmm, your result ivar in response is of type ticker which maps its 3 ivars. So uncomment A but add all 3 ivars to ticker. no ?

Thats what I gathered from the test examples (and [B.] was really grasping at straws.)
I just assumed my understanding was lacking.

Attacking it with renewed confidence from your response, 
tracing through I found the problem was the line before [A.] should have been....
    (m mapInstVar: #result) valueSchema: BittrexTicker].
 not
    (m mapInstVar: #result) valueSchema: #BittrexTicker].

now it works.
thanks for your assistance.
cheers -ben

  
Reply | Threaded
Open this post in threaded view
|

Re: REST client hints

Stephane Ducasse-3
In reply to this post by Ben Coman
https://github.com/RMODINRIA-Blockchain

Inria = 200 teams 4000 researchers :)

On Sun, Dec 10, 2017 at 9:12 AM, Ben Coman <[hidden email]> wrote:

>
>
> On 10 December 2017 at 15:27, Stephane Ducasse <[hidden email]>
> wrote:
>>
>> Tx for your report :)
>>
>> https://wordpress.com/post/pharoweekly.wordpress.com/2347
>>
>> Did you look at SmartShackles? on our inria github repo. Because
>> Santiago is extracting information from blockchains.
>>
>
> I would hesitate to call where I'm up to right now as blockchain related.
> Its just the trading api of one exchange where the items traded happen to be
> bitcoins.
> But I have a growing interest in blockchains, so thanks for the tip...
>
> I didn't see anything relevant here...  https://github.com/INRIA
> but with that product name I found...
> https://github.com/sbragagnolo/SmartShackle
>
> cheers -ben