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
Hi,
On 5 Dec 2017, at 12:15, Ben Coman <btc@openInWorld.com> 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 <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
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 <estebanlm@gmail.com> wrote:
Hi,
On 5 Dec 2017, at 12:15, Ben Coman <btc@openInWorld.com> 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
Ben,
On 5 Dec 2017, at 12:15, Ben Coman <btc@openinworld.com> 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
@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 <sven@stfx.eu> wrote:
Ben,
On 5 Dec 2017, at 12:15, Ben Coman <btc@openinworld.com> 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
On 5 Dec 2017, at 13:33, Ben Coman <btc@openinworld.com> 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/lastSuccessfu... 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-re...
btw, here is the concrete case... https://bittrex.com/home/api
cheers -ben
On 5 December 2017 at 20:06, Sven Van Caekenberghe <sven@stfx.eu> wrote:
Ben,
On 5 Dec 2017, at 12:15, Ben Coman <btc@openinworld.com> 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
On 5 December 2017 at 20:44, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 5 Dec 2017, at 13:33, Ben Coman <btc@openinworld.com> 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/lastSuccessfu... 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-re...
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-EnterprisePhar... "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 <sven@stfx.eu> wrote:
Ben,
On 5 Dec 2017, at 12:15, Ben Coman <btc@openinworld.com> 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
On 5 December 2017 at 20:44, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 5 Dec 2017, at 13:33, Ben Coman <btc@openinworld.com> 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
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 <btc@openinworld.com> wrote:
On 5 December 2017 at 20:44, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 5 Dec 2017, at 13:33, Ben Coman <btc@openinworld.com> 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/lastSuccessfu... 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-re...
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
On 10 December 2017 at 15:27, Stephane Ducasse <stepharo.self@gmail.com> 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
https://github.com/RMODINRIA-Blockchain Inria = 200 teams 4000 researchers :) On Sun, Dec 10, 2017 at 9:12 AM, Ben Coman <btc@openinworld.com> wrote:
On 10 December 2017 at 15:27, Stephane Ducasse <stepharo.self@gmail.com> 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
On 10 Dec 2017, at 04:45, Ben Coman <btc@openinworld.com> wrote:
On 5 December 2017 at 20:44, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 5 Dec 2017, at 13:33, Ben Coman <btc@openinworld.com> 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/lastSuccessfu... 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-re...
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
Hi Sven (et al), On 10 December 2017 at 11:45, Ben Coman <btc@openinworld.com> 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
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 <btc@openinworld.com> wrote:
Hi Sven (et al),
On 10 December 2017 at 11:45, Ben Coman <btc@openinworld.com> 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
On 13 December 2017 at 14:40, Sven Van Caekenberghe <sven@stfx.eu> 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 <btc@openinworld.com> wrote:
Hi Sven (et al),
On 10 December 2017 at 11:45, Ben Coman <btc@openinworld.com> 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
On 13 Dec 2017, at 07:49, Ben Coman <btc@openinworld.com> wrote:
On 13 December 2017 at 14:40, Sven Van Caekenberghe <sven@stfx.eu> 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 <btc@openinworld.com> wrote:
Hi Sven (et al),
On 10 December 2017 at 11:45, Ben Coman <btc@openinworld.com> 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
On 13 Dec 2017, at 05:37, Ben Coman <btc@openinworld.com> wrote:
Hi Sven (et al),
On 10 December 2017 at 11:45, Ben Coman <btc@openinworld.com> 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 <sven@stfx.eu> wrote:
On 13 Dec 2017, at 07:49, Ben Coman <btc@openinworld.com> wrote:
On 13 December 2017 at 14:40, Sven Van Caekenberghe <sven@stfx.eu> 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
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
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 <pdebruic@gmail.com> 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
participants (7)
-
Ben Coman -
Esteban Lorenzano -
Miguel Moquillon -
Paul DeBruicker -
Peter Uhnák -
Stephane Ducasse -
Sven Van Caekenberghe