[aerogear-dev] New Android Pipeline

classic Classic list List threaded Threaded
26 messages Options
12
Reply | Threaded
Open this post in threaded view
|

[aerogear-dev] New Android Pipeline

Daniel Passos-2
Hi guys 

I did some changes in the android library based on iOS stuff, it's closer to the pipeline adapter implementation. I would appreciate feedback and review.


Thanks,
Passos

_______________________________________________
aerogear-dev mailing list
[hidden email]
https://lists.jboss.org/mailman/listinfo/aerogear-dev
Reply | Threaded
Open this post in threaded view
|

Re: [aerogear-dev] New Android Pipeline

Marko Strukelj
Great!

I'll check it out later today.

Thanks,

- marko


----- Original Message -----

>
>
> Hi guys
>
>
> I did some changes in the android library based on iOS stuff, it's
> closer to the pipeline adapter implementation. I would appreciate
> feedback and review.
>
>
> https://github.com/aerogear/aerogear-android/pull/1
> https://github.com/aerogear/aerogear-android-todo/pull/1
>
>
> Thanks,
> Passos
> _______________________________________________
> aerogear-dev mailing list
> [hidden email]
> https://lists.jboss.org/mailman/listinfo/aerogear-dev
>
_______________________________________________
aerogear-dev mailing list
[hidden email]
https://lists.jboss.org/mailman/listinfo/aerogear-dev
Reply | Threaded
Open this post in threaded view
|

Re: [aerogear-dev] New Android Pipeline

Glen Daniels
Cool, I'll pull it down and read through on the flight home tonight!

--G

Marko Strukelj <[hidden email]> wrote:
Great!

I'll check it out later today.

Thanks,

- marko


----- Original Message -----


Hi guys


I did some changes in the android library based on iOS stuff, it's
closer to the pipeline adapter implementation. I would appreciate
feedback and review.


https://github.com/aerogear/aerogear-android/pull/1
https://github.com/aerogear/aerogear-android-todo/pull/1


Thanks,
Passos


aerogear-dev mailing list
[hidden email]
https://lists.jboss.org/mailman/listinfo/aerogear-dev



aerogear-dev mailing list
[hidden email]
https://lists.jboss.org/mailman/listinfo/aerogear-dev

--
Sent from my Android phone with K-9 Mail. Please excuse my brevity.
_______________________________________________
aerogear-dev mailing list
[hidden email]
https://lists.jboss.org/mailman/listinfo/aerogear-dev
Reply | Threaded
Open this post in threaded view
|

Re: [aerogear-dev] New Android Pipeline

Marko Strukelj
In reply to this post by Daniel Passos-2

Some comments ... (I haven't looked at this that closely before so there are some things here that relate to much earlier commits).


- Logging seems to be the only android specific thing in the library ATM. I'm thinking that maybe we could treat it as a generic java client library using slf4j maybe, and provide pluggability to wire it up to android.util.Log on Android.

- I think Matthias noticed that the API is nicely aligned with the one for iOS, which is exactly what we want. (I didn't compare, so I can't say :)

- Looking at HttpRestProvider get() method doesn't take an id - it's an operation that retrieves a list of all instances - presuming URL points to something like http://host/task, and not to something like http://host/task/100.
(https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/core/HttpRestProvider.java#L46)

That makes sense in the HTTP meaning of GET.

It means, that if we want to get a single instance of a resource (i.e. Task#100) we have to create a new HttpRestProvider with a new URL.

For delete(), OTOH we can execute it with a specific id, and the same goes for put() to perform an update of a child resource.

So we have GET, and POST operations performed on a 'parent' resource, and DELETE, and PUT operations performed on child resources. This asymmetry is confusing to me ... non-intuitive.

- I wouldn't eat exceptions, and return null even in a not-yet-real exception handling :)
(https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/core/HttpRestProvider.java#L86)

Really, either don't catch it, or if you do catch it, rethrow it as is, or wrap into another - normally RuntimeException is perfectly fine, so you don't pollute your API with throws declarations.

- I would use IllegalArgumentException here:
https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/pipeline/AdapterFactory.java#L34

It's more appropriate, as it's a standard pattern for this kind of use-case. By convention UnsupportedOperationException is used for empty methods where interface contract is not fully supported.

- We have to do something about this "getId"
(https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/pipeline/RestAdapter.java#L76)

One idea is to have an annotation - @Id. But scanning for annotations needs to be done at init time or lazily on first use ...
So maybe we could have an abstract base class with abstract method getId() that every data object has to extend, and implement. A simpler, and more robust solution actually, as compiler will enforce it so there is no way for not providing one, as could happen with forgotten or wrong placement of @Id annotation for example.  


That's about it for now ... :)
 
- marko


----- Original Message -----

>
>
> Hi guys
>
>
> I did some changes in the android library based on iOS stuff, it's
> closer to the pipeline adapter implementation. I would appreciate
> feedback and review.
>
>
> https://github.com/aerogear/aerogear-android/pull/1
> https://github.com/aerogear/aerogear-android-todo/pull/1
>
>
> Thanks,
> Passos
> _______________________________________________
> aerogear-dev mailing list
> [hidden email]
> https://lists.jboss.org/mailman/listinfo/aerogear-dev
>
_______________________________________________
aerogear-dev mailing list
[hidden email]
https://lists.jboss.org/mailman/listinfo/aerogear-dev
Reply | Threaded
Open this post in threaded view
|

Re: [aerogear-dev] New Android Pipeline

Matthias Wessendorf
Hello,


On Wed, Oct 3, 2012 at 12:37 AM, Marko Strukelj <[hidden email]> wrote:

>
> Some comments ... (I haven't looked at this that closely before so there are some things here that relate to much earlier commits).
>
>
> - Logging seems to be the only android specific thing in the library ATM. I'm thinking that maybe we could treat it as a generic java client library using slf4j maybe, and provide pluggability to wire it up to android.util.Log on Android.
>
> - I think Matthias noticed that the API is nicely aligned with the one for iOS, which is exactly what we want. (I didn't compare, so I can't say :)
>
> - Looking at HttpRestProvider get() method doesn't take an id - it's an operation that retrieves a list of all instances - presuming URL points to something like http://host/task, and not to something like http://host/task/100.
> (https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/core/HttpRestProvider.java#L46)
>
> That makes sense in the HTTP meaning of GET.


I'd use plural for endpoint URIs (e.g. /tasks and not /task)

We need get() for retrieving _all_ items and get(id) for retrieving a
single object (=> /tasks/{id}).
(We also need 'queries', eventually)

Regarding iOS... I left a TODO on the more high level 'filter' read
(not implemented in the rest adapter)
=> https://github.com/aerogear/aerogear-ios/blob/master/AeroGear-iOS/AeroGear-iOS/AGPipe.h#L55

The 'filterObject' argument *could* be:
 - an id to look up a single entity
 - some more complex query object (e.g. a range or what not)
==> this all would translate in to a 'get()' invocation, where the
method takes an argument.

For looking up a single entity, of course I could have added a
'read(id)', which invokes an underlying 'http.get(id)'; Perhaps I add
that read(id) now :)

Anyways, back to Android... get() is fine for all, BUT get(id) is also needed.

>
> It means, that if we want to get a single instance of a resource (i.e. Task#100) we have to create a new HttpRestProvider with a new URL.
>
> For delete(), OTOH we can execute it with a specific id, and the same goes for put() to perform an update of a child resource.


Correct, a put on /tasks does make no sense. A delete on /tasks would
(I guess in most cases) mean all items are deleted, which feels
wrong....

So IMO these put/delete operations make more sense on URIs, like
/tasks/{id} (update / remove a single item).



>
> So we have GET, and POST operations performed on a 'parent' resource, and DELETE, and PUT operations performed on child resources. This asymmetry is confusing to me ... non-intuitive.

Hrm... I disagree, as indicated in my sentences on put/delete above...

I like Stefan's image on restful URIs:
http://www.infoq.com/resource/articles/rest-introduction/en/resources/figure2.jpg

The above picture 'models' URIs for this UML diagram:
http://www.infoq.com/resource/articles/rest-introduction/en/resources/figure1.jpg

(Taken from this article => http://www.infoq.com/articles/rest-introduction)

However the figure2.jpg shows as well, that there maybe cases where a
DELETE on /tasks (or '/customers/{id}/orders') and a POST on
/tasks/{id} (or '/orders/{id}') can make sense.

=> We need to cover that too...
If I recall correctly those two 'corner cases' are also not covered by
the JavaScript library.


> - I wouldn't eat exceptions, and return null even in a not-yet-real exception handling :)
> (https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/core/HttpRestProvider.java#L86)
>
> Really, either don't catch it, or if you do catch it, rethrow it as is, or wrap into another - normally RuntimeException is perfectly fine, so you don't pollute your API with throws declarations.


+1 on this comment - but he added a TODO already ;)


>
> - I would use IllegalArgumentException here:
> https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/pipeline/AdapterFactory.java#L34
>
> It's more appropriate, as it's a standard pattern for this kind of use-case. By convention UnsupportedOperationException is used for empty methods where interface contract is not fully supported.
>

+1



> - We have to do something about this "getId"
> (https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/pipeline/RestAdapter.java#L76)
>
> One idea is to have an annotation - @Id. But scanning for annotations needs to be done at init time or lazily on first use ...
> So maybe we could have an abstract base class with abstract method getId() that every data object has to extend, and implement. A simpler, and more robust solution actually, as compiler will enforce it so there is no way for not providing one, as could happen with forgotten or wrong placement of @Id annotation for example.
>

I think the problem here is that not every object has an 'id', the
field could be name 'recordId' - In JavaScript this is configurable.
iOS has a TODO here...


-Matthias



>
> That's about it for now ... :)
>
> - marko
>
>
> ----- Original Message -----
>>
>>
>> Hi guys
>>
>>
>> I did some changes in the android library based on iOS stuff, it's
>> closer to the pipeline adapter implementation. I would appreciate
>> feedback and review.
>>
>>
>> https://github.com/aerogear/aerogear-android/pull/1
>> https://github.com/aerogear/aerogear-android-todo/pull/1
>>
>>
>> Thanks,
>> Passos
>> _______________________________________________
>> aerogear-dev mailing list
>> [hidden email]
>> https://lists.jboss.org/mailman/listinfo/aerogear-dev
>>
> _______________________________________________
> aerogear-dev mailing list
> [hidden email]
> https://lists.jboss.org/mailman/listinfo/aerogear-dev



--
Matthias Wessendorf

blog: http://matthiaswessendorf.wordpress.com/
sessions: http://www.slideshare.net/mwessendorf
twitter: http://twitter.com/mwessendorf

_______________________________________________
aerogear-dev mailing list
[hidden email]
https://lists.jboss.org/mailman/listinfo/aerogear-dev
Reply | Threaded
Open this post in threaded view
|

Re: [aerogear-dev] New Android Pipeline

Glen Daniels
In reply to this post by Daniel Passos-2
Hi Passos,

A few comments below.  I recognize that some of these things are derived
from the JS implementation, which was the source for the iOS
implementation - so some of these are questions about the the API in
general as opposed to Android in particular.

* I'm not sure I like creating an initial Pipe at Pipeline-creation
time.  I guess this is meant as a shortcut, but in fact I think it
confuses things.  Since we need a line of code to get the Pipe anyway,
I'd rather it be consistent and look like this:

Pipeline pipeline = new Pipeline(baseURL);
Pipe<Task> tasks = pipeline.add("tasks", Task[].class);
Pipe<Tag> tags = pipeline.add("tags", Tag[].class);

...rather than special-casing the first "tasks" pipe and using
pipeline.get("tasks") to retrieve it.

* Another consistency/symmetry issue (ah, I just read Marko's comments
and I see he mentions the same thing): I think it's odd that get() and
post() in HttpRestProvider can't take id arguments, and therefore must
always work at the collection, as opposed to the individual resource
level.  Simply adding overloads with an id argument would solve this I
think, but maybe better to always require the id argument and allow null
to access the collection.

* HttpRestProvider again: the post() method at least (and perhaps also
put/delete) should probably return a value so we can get server data
back to the caller if appropriate.

* I'm not quite sure I understand why we need the HttpProvider interface
at all.  What other implementations of this (that aren't
HttpRestProvider) are there going to be?  We could get rid of it and
just bake HTTP functionality into RestAdapter directly, no?

* We should discuss testing + mocking and write down some common tools +
patterns.

That's it for the moment...  I'm in Chicago on the way home to Boston
right now, and will be tuned back in on Wednesday afternoon.  I'll try
to write up some more Android thoughts at that point, specifically about
the Android-specific surface area of AeroGear.

Time to catch my last plane!

Best,
--Glen

On 10/2/12 7:12 AM, Daniel Passos wrote:

> Hi guys
>
> I did some changes in the android library based on iOS stuff, it's
> closer to the pipeline adapter implementation. I would appreciate
> feedback and review.
>
> https://github.com/aerogear/aerogear-android/pull/1
> https://github.com/aerogear/aerogear-android-todo/pull/1
>
> Thanks,
> Passos
>
>
> _______________________________________________
> aerogear-dev mailing list
> [hidden email]
> https://lists.jboss.org/mailman/listinfo/aerogear-dev
>

_______________________________________________
aerogear-dev mailing list
[hidden email]
https://lists.jboss.org/mailman/listinfo/aerogear-dev
Reply | Threaded
Open this post in threaded view
|

Re: [aerogear-dev] New Android Pipeline

Matthias Wessendorf
On Wed, Oct 3, 2012 at 4:54 AM, Glen Daniels <[hidden email]> wrote:

> Hi Passos,
>
> A few comments below.  I recognize that some of these things are derived
> from the JS implementation, which was the source for the iOS
> implementation - so some of these are questions about the the API in
> general as opposed to Android in particular.
>
> * I'm not sure I like creating an initial Pipe at Pipeline-creation
> time.  I guess this is meant as a shortcut, but in fact I think it
> confuses things.  Since we need a line of code to get the Pipe anyway,
> I'd rather it be consistent and look like this:
>
> Pipeline pipeline = new Pipeline(baseURL);
> Pipe<Task> tasks = pipeline.add("tasks", Task[].class);
> Pipe<Tag> tags = pipeline.add("tags", Tag[].class);
>
> ...rather than special-casing the first "tasks" pipe and using
> pipeline.get("tasks") to retrieve it.

I think I can see room for improvement here too... At least on the
iOS/Android side of things...

I guess the following 'scenario' is fine:
 => creating an empty pipeline with just the base URL, where adding
new pipes is only done with an 'add' method.

> * Another consistency/symmetry issue (ah, I just read Marko's comments
> and I see he mentions the same thing): I think it's odd that get() and
> post() in HttpRestProvider can't take id arguments, and therefore must
> always work at the collection, as opposed to the individual resource
> level.  Simply adding overloads with an id argument would solve this I
> think, but maybe better to always require the id argument and allow null
> to access the collection.

Right, the get(id) is needed and as seen in the references to Stefans
article (see previous email) a post(id) is also needed on the API, in
general.
(in the same way, how a DELETE on a 'collection' is needed => DELTE on
/customers/{id}/orders cancels all orders for the customer)

>
> * HttpRestProvider again: the post() method at least (and perhaps also
> put/delete) should probably return a value so we can get server data
> back to the caller if appropriate.
>
> * I'm not quite sure I understand why we need the HttpProvider interface
> at all.  What other implementations of this (that aren't
> HttpRestProvider) are there going to be?  We could get rid of it and
> just bake HTTP functionality into RestAdapter directly, no?

I think this comes from the iOS layer...
I added a AGHttpClient, which is a very tiny subclass of
AFNetworking's HTTP client (recommended pattern by AFNetworking). The
subclass does (right now) not too much...

So the 'restful adapter' basically delegates all HTTP calls to the AGHttpClient:
https://github.com/aerogear/aerogear-ios/blob/master/AeroGear-iOS/AeroGear-iOS/AGRestAdapter.m


-Matthias

> * We should discuss testing + mocking and write down some common tools +
> patterns.
>
> That's it for the moment...  I'm in Chicago on the way home to Boston
> right now, and will be tuned back in on Wednesday afternoon.  I'll try
> to write up some more Android thoughts at that point, specifically about
> the Android-specific surface area of AeroGear.
>
> Time to catch my last plane!
>
> Best,
> --Glen
>
> On 10/2/12 7:12 AM, Daniel Passos wrote:
>> Hi guys
>>
>> I did some changes in the android library based on iOS stuff, it's
>> closer to the pipeline adapter implementation. I would appreciate
>> feedback and review.
>>
>> https://github.com/aerogear/aerogear-android/pull/1
>> https://github.com/aerogear/aerogear-android-todo/pull/1
>>
>> Thanks,
>> Passos
>>
>>
>> _______________________________________________
>> aerogear-dev mailing list
>> [hidden email]
>> https://lists.jboss.org/mailman/listinfo/aerogear-dev
>>
>
> _______________________________________________
> aerogear-dev mailing list
> [hidden email]
> https://lists.jboss.org/mailman/listinfo/aerogear-dev



--
Matthias Wessendorf

blog: http://matthiaswessendorf.wordpress.com/
sessions: http://www.slideshare.net/mwessendorf
twitter: http://twitter.com/mwessendorf
_______________________________________________
aerogear-dev mailing list
[hidden email]
https://lists.jboss.org/mailman/listinfo/aerogear-dev
Reply | Threaded
Open this post in threaded view
|

Re: [aerogear-dev] New Android Pipeline

Marko Strukelj

> On Wed, Oct 3, 2012 at 4:54 AM, Glen Daniels <[hidden email]>
> wrote:
> > Hi Passos,
> >
> > A few comments below.  I recognize that some of these things are
> > derived
> > from the JS implementation, which was the source for the iOS
> > implementation - so some of these are questions about the the API
> > in
> > general as opposed to Android in particular.
> >
> > * I'm not sure I like creating an initial Pipe at Pipeline-creation
> > time.  I guess this is meant as a shortcut, but in fact I think it
> > confuses things.  Since we need a line of code to get the Pipe
> > anyway,
> > I'd rather it be consistent and look like this:
> >
> > Pipeline pipeline = new Pipeline(baseURL);
> > Pipe<Task> tasks = pipeline.add("tasks", Task[].class);
> > Pipe<Tag> tags = pipeline.add("tags", Tag[].class);
> >
> > ...rather than special-casing the first "tasks" pipe and using
> > pipeline.get("tasks") to retrieve it.
>
> I think I can see room for improvement here too... At least on the
> iOS/Android side of things...
>
> I guess the following 'scenario' is fine:
>  => creating an empty pipeline with just the base URL, where adding
> new pipes is only done with an 'add' method.
>
> > * Another consistency/symmetry issue (ah, I just read Marko's
> > comments
> > and I see he mentions the same thing): I think it's odd that get()
> > and
> > post() in HttpRestProvider can't take id arguments, and therefore
> > must
> > always work at the collection, as opposed to the individual
> > resource
> > level.  Simply adding overloads with an id argument would solve
> > this I
> > think, but maybe better to always require the id argument and allow
> > null
> > to access the collection.
>
> Right, the get(id) is needed and as seen in the references to Stefans
> article (see previous email) a post(id) is also needed on the API, in
> general.
> (in the same way, how a DELETE on a 'collection' is needed => DELTE
> on
> /customers/{id}/orders cancels all orders for the customer)

Do you mean to define DELETE operation as a 'cancel' (not actually as a 'remove' - more like an 'update' of existing entity)?
Maybe that is a blackbox for this discussion, the important thing is that GET on collection returns empty after a successful DELETE operation.
But still it is an interesting question to me ... what are best practices when it comes to mapping a RESTful view of an application to the actual model behind - the controller in-between the view and the model could map these GET, DELETE, PUT ... to some business logic operations - like 'cancel' the orders, as opposed to 'delete' them ...

>
> >
> > * HttpRestProvider again: the post() method at least (and perhaps
> > also
> > put/delete) should probably return a value so we can get server
> > data
> > back to the caller if appropriate.
> >
> > * I'm not quite sure I understand why we need the HttpProvider
> > interface
> > at all.  What other implementations of this (that aren't
> > HttpRestProvider) are there going to be?  We could get rid of it
> > and
> > just bake HTTP functionality into RestAdapter directly, no?
>
> I think this comes from the iOS layer...
> I added a AGHttpClient, which is a very tiny subclass of
> AFNetworking's HTTP client (recommended pattern by AFNetworking). The
> subclass does (right now) not too much...
>
> So the 'restful adapter' basically delegates all HTTP calls to the
> AGHttpClient:
> https://github.com/aerogear/aerogear-ios/blob/master/AeroGear-iOS/AeroGear-iOS/AGRestAdapter.m
>

I asked myself the same question. Anyway it makes sense to make an API abstract in a way that allows plugging in a different implementation.
It could maybe be HttpClient as opposed to SFTPClient? Or HttpRestClient as opposed to HttpSOAPClient?


>
> -Matthias
>
> > * We should discuss testing + mocking and write down some common
> > tools +
> > patterns.
> >
> > That's it for the moment...  I'm in Chicago on the way home to
> > Boston
> > right now, and will be tuned back in on Wednesday afternoon.  I'll
> > try
> > to write up some more Android thoughts at that point, specifically
> > about
> > the Android-specific surface area of AeroGear.
> >
> > Time to catch my last plane!
> >
> > Best,
> > --Glen
> >
> > On 10/2/12 7:12 AM, Daniel Passos wrote:
> >> Hi guys
> >>
> >> I did some changes in the android library based on iOS stuff, it's
> >> closer to the pipeline adapter implementation. I would appreciate
> >> feedback and review.
> >>
> >> https://github.com/aerogear/aerogear-android/pull/1
> >> https://github.com/aerogear/aerogear-android-todo/pull/1
> >>
> >> Thanks,
> >> Passos
> >>
> >>
> >> _______________________________________________
> >> aerogear-dev mailing list
> >> [hidden email]
> >> https://lists.jboss.org/mailman/listinfo/aerogear-dev
> >>
> >
> > _______________________________________________
> > aerogear-dev mailing list
> > [hidden email]
> > https://lists.jboss.org/mailman/listinfo/aerogear-dev
>
>
>
> --
> Matthias Wessendorf
>
> blog: http://matthiaswessendorf.wordpress.com/
> sessions: http://www.slideshare.net/mwessendorf
> twitter: http://twitter.com/mwessendorf
> _______________________________________________
> aerogear-dev mailing list
> [hidden email]
> https://lists.jboss.org/mailman/listinfo/aerogear-dev
>
_______________________________________________
aerogear-dev mailing list
[hidden email]
https://lists.jboss.org/mailman/listinfo/aerogear-dev
Reply | Threaded
Open this post in threaded view
|

Re: [aerogear-dev] New Android Pipeline

Daniel Passos-2
In reply to this post by Marko Strukelj
2012/10/2 Marko Strukelj <[hidden email]>

Some comments ... (I haven't looked at this that closely before so there are some things here that relate to much earlier commits).

- Logging seems to be the only android specific thing in the library ATM. I'm thinking that maybe we could treat it as a generic java client library using slf4j maybe, and provide pluggability to wire it up to android.util.Log on Android.

I don't know. I need to think about this.
 
- I think Matthias noticed that the API is nicely aligned with the one for iOS, which is exactly what we want. (I didn't compare, so I can't say :)

- Looking at HttpRestProvider get() method doesn't take an id - it's an operation that retrieves a list of all instances - presuming URL points to something like http://host/task, and not to something like http://host/task/100.
(https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/core/HttpRestProvider.java#L46)

That makes sense in the HTTP meaning of GET.

It means, that if we want to get a single instance of a resource (i.e. Task#100) we have to create a new HttpRestProvider with a new URL.

Make sense. Addind get(id)
 
For delete(), OTOH we can execute it with a specific id, and the same goes for put() to perform an update of a child resource.

So we have GET, and POST operations performed on a 'parent' resource, and DELETE, and PUT operations performed on child resources. This asymmetry is confusing to me ... non-intuitive.

Me too, I did it just because we do not have a way to find the id dynamically yet
 
- I wouldn't eat exceptions, and return null even in a not-yet-real exception handling :)
(https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/core/HttpRestProvider.java#L86)

Really, either don't catch it, or if you do catch it, rethrow it as is, or wrap into another - normally RuntimeException is perfectly fine, so you don't pollute your API with throws declarations.

I vote to throw RuntimeException
 
- I would use IllegalArgumentException here:
https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/pipeline/AdapterFactory.java#L34

It's more appropriate, as it's a standard pattern for this kind of use-case. By convention UnsupportedOperationException is used for empty methods where interface contract is not fully supported.

Ok!
 
- We have to do something about this "getId"
(https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/pipeline/RestAdapter.java#L76)

One idea is to have an annotation - @Id. But scanning for annotations needs to be done at init time or lazily on first use ...
So maybe we could have an abstract base class with abstract method getId() that every data object has to extend, and implement. A simpler, and more robust solution actually, as compiler will enforce it so there is no way for not providing one, as could happen with forgotten or wrong placement of @Id annotation for example.

 
That's about it for now ... :)

- marko

----- Original Message -----
>
>
> Hi guys
>
>
> I did some changes in the android library based on iOS stuff, it's
> closer to the pipeline adapter implementation. I would appreciate
> feedback and review.
>
>
> https://github.com/aerogear/aerogear-android/pull/1
> https://github.com/aerogear/aerogear-android-todo/pull/1
>
>
> Thanks,
> Passos
> _______________________________________________
_______________________________________________


_______________________________________________
aerogear-dev mailing list
[hidden email]
https://lists.jboss.org/mailman/listinfo/aerogear-dev
Reply | Threaded
Open this post in threaded view
|

Re: [aerogear-dev] New Android Pipeline

Daniel Passos-2
In reply to this post by Glen Daniels
2012/10/2 Glen Daniels <[hidden email]>
Hi Passos,

A few comments below.  I recognize that some of these things are derived
from the JS implementation, which was the source for the iOS
implementation - so some of these are questions about the the API in
general as opposed to Android in particular.

* I'm not sure I like creating an initial Pipe at Pipeline-creation
time.  I guess this is meant as a shortcut, but in fact I think it
confuses things.  Since we need a line of code to get the Pipe anyway,
I'd rather it be consistent and look like this:

Pipeline pipeline = new Pipeline(baseURL);
Pipe<Task> tasks = pipeline.add("tasks", Task[].class);
Pipe<Tag> tags = pipeline.add("tags", Tag[].class);

I'm not sure too, If create a pipe with a pipeline is the best option for this. If you consider the legibility, is an ugly way to search for a thing you' ve created, but i thinking if make sense create a pipeline without pipe.

...rather than special-casing the first "tasks" pipe and using
pipeline.get("tasks") to retrieve it.

* Another consistency/symmetry issue (ah, I just read Marko's comments
and I see he mentions the same thing): I think it's odd that get() and
post() in HttpRestProvider can't take id arguments, and therefore must
always work at the collection, as opposed to the individual resource
level.  Simply adding overloads with an id argument would solve this I
think, but maybe better to always require the id argument and allow null
to access the collection.

* HttpRestProvider again: the post() method at least (and perhaps also
put/delete) should probably return a value so we can get server data
back to the caller if appropriate.

I see your point and is a good idea.
 
* I'm not quite sure I understand why we need the HttpProvider interface
at all.  What other implementations of this (that aren't
HttpRestProvider) are there going to be?  We could get rid of it and
just bake HTTP functionality into RestAdapter directly, no?

I create that imagining a decloupe for in the future, test the adapter with mock/stubs provider

* We should discuss testing + mocking and write down some common tools +
patterns.

That's it for the moment...  I'm in Chicago on the way home to Boston
right now, and will be tuned back in on Wednesday afternoon.  I'll try
to write up some more Android thoughts at that point, specifically about
the Android-specific surface area of AeroGear.

Time to catch my last plane!

Best,
--Glen

On 10/2/12 7:12 AM, Daniel Passos wrote:
> Hi guys
>
> I did some changes in the android library based on iOS stuff, it's
> closer to the pipeline adapter implementation. I would appreciate
> feedback and review.
>
> https://github.com/aerogear/aerogear-android/pull/1
> https://github.com/aerogear/aerogear-android-todo/pull/1
>
> Thanks,
> Passos
>
>
> _______________________________________________
> aerogear-dev mailing list
> [hidden email]
> https://lists.jboss.org/mailman/listinfo/aerogear-dev
>

_______________________________________________
aerogear-dev mailing list
[hidden email]
https://lists.jboss.org/mailman/listinfo/aerogear-dev


_______________________________________________
aerogear-dev mailing list
[hidden email]
https://lists.jboss.org/mailman/listinfo/aerogear-dev
Reply | Threaded
Open this post in threaded view
|

Re: [aerogear-dev] New Android Pipeline

Kris Borchers

On Oct 3, 2012, at 7:29 AM, Daniel Passos <[hidden email]> wrote:

2012/10/2 Glen Daniels <[hidden email]>
Hi Passos,

A few comments below.  I recognize that some of these things are derived
from the JS implementation, which was the source for the iOS
implementation - so some of these are questions about the the API in
general as opposed to Android in particular.

* I'm not sure I like creating an initial Pipe at Pipeline-creation
time.  I guess this is meant as a shortcut, but in fact I think it
confuses things.  Since we need a line of code to get the Pipe anyway,
I'd rather it be consistent and look like this:

Pipeline pipeline = new Pipeline(baseURL);
Pipe<Task> tasks = pipeline.add("tasks", Task[].class);
Pipe<Tag> tags = pipeline.add("tags", Tag[].class);

I'm not sure too, If create a pipe with a pipeline is the best option for this. If you consider the legibility, is an ugly way to search for a thing you' ve created, but i thinking if make sense create a pipeline without pipe.

Sorry if I wasn't clear in my examples or usage but in JS, it is completely possible to create a pipeline without an initial pipe and then add them when you need them.

Ex.

// Create pipeline
var x = aerogear.pipeline();
// Add Pipe
x.add( "test" );

// Use the pipe
var testPipe = x.pipes.test;
testPipe.read(…);

OR

x.pipes.test.read(…);


...rather than special-casing the first "tasks" pipe and using
pipeline.get("tasks") to retrieve it.

* Another consistency/symmetry issue (ah, I just read Marko's comments
and I see he mentions the same thing): I think it's odd that get() and
post() in HttpRestProvider can't take id arguments, and therefore must
always work at the collection, as opposed to the individual resource
level.  Simply adding overloads with an id argument would solve this I
think, but maybe better to always require the id argument and allow null
to access the collection.

* HttpRestProvider again: the post() method at least (and perhaps also
put/delete) should probably return a value so we can get server data
back to the caller if appropriate.

I see your point and is a good idea.
 
* I'm not quite sure I understand why we need the HttpProvider interface
at all.  What other implementations of this (that aren't
HttpRestProvider) are there going to be?  We could get rid of it and
just bake HTTP functionality into RestAdapter directly, no?

I create that imagining a decloupe for in the future, test the adapter with mock/stubs provider

* We should discuss testing + mocking and write down some common tools +
patterns.

That's it for the moment...  I'm in Chicago on the way home to Boston
right now, and will be tuned back in on Wednesday afternoon.  I'll try
to write up some more Android thoughts at that point, specifically about
the Android-specific surface area of AeroGear.

Time to catch my last plane!

Best,
--Glen

On 10/2/12 7:12 AM, Daniel Passos wrote:
> Hi guys
>
> I did some changes in the android library based on iOS stuff, it's
> closer to the pipeline adapter implementation. I would appreciate
> feedback and review.
>
> https://github.com/aerogear/aerogear-android/pull/1
> https://github.com/aerogear/aerogear-android-todo/pull/1
>
> Thanks,
> Passos
>
>
> _______________________________________________
> aerogear-dev mailing list
> [hidden email]
> https://lists.jboss.org/mailman/listinfo/aerogear-dev
>

_______________________________________________
aerogear-dev mailing list
[hidden email]
https://lists.jboss.org/mailman/listinfo/aerogear-dev

_______________________________________________
aerogear-dev mailing list
[hidden email]
https://lists.jboss.org/mailman/listinfo/aerogear-dev


_______________________________________________
aerogear-dev mailing list
[hidden email]
https://lists.jboss.org/mailman/listinfo/aerogear-dev
Reply | Threaded
Open this post in threaded view
|

Re: [aerogear-dev] New Android Pipeline

Matthias Wessendorf


On Wednesday, October 3, 2012, Kris Borchers wrote:

On Oct 3, 2012, at 7:29 AM, Daniel Passos <<a href="javascript:_e({}, &#39;cvml&#39;, &#39;daniel@passos.me&#39;);" target="_blank">daniel@...> wrote:

2012/10/2 Glen Daniels <<a href="javascript:_e({}, &#39;cvml&#39;, &#39;glen@thoughtcraft.com&#39;);" target="_blank">glen@...>
Hi Passos,

A few comments below.  I recognize that some of these things are derived
from the JS implementation, which was the source for the iOS
implementation - so some of these are questions about the the API in
general as opposed to Android in particular.

* I'm not sure I like creating an initial Pipe at Pipeline-creation
time.  I guess this is meant as a shortcut, but in fact I think it
confuses things.  Since we need a line of code to get the Pipe anyway,
I'd rather it be consistent and look like this:

Pipeline pipeline = new Pipeline(baseURL);
Pipe<Task> tasks = pipeline.add("tasks", Task[].class);
Pipe<Tag> tags = pipeline.add("tags", Tag[].class);

I'm not sure too, If create a pipe with a pipeline is the best option for this. If you consider the legibility, is an ugly way to search for a thing you' ve created, but i thinking if make sense create a pipeline without pipe.

Sorry if I wasn't clear in my examples or usage but in JS, it is completely possible to create a pipeline without an initial pipe and then add them when you need them.

Hrm, I think I saw that the initial pipe was required; anyways will update iOS. A code review or API proposal would have caught that earlier ;)

-Matthias



 

Ex.

// Create pipeline
var x = aerogear.pipeline();
// Add Pipe
x.add( "test" );

// Use the pipe
var testPipe = x.pipes.test;
testPipe.read(…);

OR

x.pipes.test.read(…);


...rather than special-casing the first "tasks" pipe and using
pipeline.get("tasks") to retrieve it.

* Another consistency/symmetry issue (ah, I just read Marko's comments
and I see he mentions the same thing): I think it's odd that get() and
post() in HttpRestProvider can't take id arguments, and therefore must
always work at the collection, as opposed to the individual resource
level.  Simply adding overloads with an id argument would solve this I
think, but maybe better to always require the id argument and allow null
to access the collection.

* HttpRestProvider again: the post() method at least (and perhaps also
put/delete) should probably return a value so we can get server data
back to the caller if appropriate.

I see your point and is a good idea.
 
* I'm not quite sure I understand why we need the HttpProvider interface
at all.  What other implementations of this (that aren't
HttpRestProvider) are there going to be?  We could get rid of it and
just bake HTTP functionality into RestAdapter directly, no?

I create that imagining a decloupe for in the future, test the adapter with mock/stubs provider

* We should discuss testing + mocking and write down some common tools +
patterns.

That's it for the moment...  I'm in Chicago on the way home to Boston
right now, and will be tuned back in on Wednesday afternoon.  I'll try
to write up some more Android thoughts at that point, specifically about
the Android-specific surface area of AeroGear.

Time to catch my last plane!

Best,
--Glen

On 10/2/12 7:12 AM, Daniel Passos wrote:
> Hi guys
>
> I did some changes in the android library based on iOS stuff, it's
> closer to the pipeline adapter implementation. I would appreciate
> feedback and review.
>
> https://github.com/aerogear/aerogear-android/pull/1
> https://github.com/aerogear/aerogear-android-todo/pull/1
>
> Thanks,
> Passos
>
>
> _______________________________________________
> aerogear-dev mailing list
> <a href="javascript:_e({}, &#39;cvml&#39;, &#39;aerogear-dev@lists.jboss.org&#39;);" target="_blank">aerogear-dev@...
> https://lists.jboss.org/mailman/listinfo/aerogear-dev
>

_______________________________________________
aerogear-dev mailing list
<a href="javascript:_e({}, &#39;cvml&#39;, &#39;aerogear-dev@lists.jboss.org&#39;);" target="_blank">aerogear-dev@...
https://lists.jboss.org/mailman/listinfo/aerogear-dev

_______________________________________________
aerogear-dev mailing list
<a href="javascript:_e({}, &#39;cvml&#39;, &#39;aerogear-dev@lists.jboss.org&#39;);" target="_blank">aerogear-dev@...
https://lists.jboss.org/mailman/listinfo/aerogear-dev



--
Sent from Gmail Mobile

_______________________________________________
aerogear-dev mailing list
[hidden email]
https://lists.jboss.org/mailman/listinfo/aerogear-dev
Reply | Threaded
Open this post in threaded view
|

Re: [aerogear-dev] New Android Pipeline

Marko Strukelj
In reply to this post by Matthias Wessendorf


> Hello,
>
>
> On Wed, Oct 3, 2012 at 12:37 AM, Marko Strukelj <[hidden email]>
> wrote:
> >
> > Some comments ... (I haven't looked at this that closely before so
> > there are some things here that relate to much earlier commits).
> >
> >
> > - Logging seems to be the only android specific thing in the
> > library ATM. I'm thinking that maybe we could treat it as a
> > generic java client library using slf4j maybe, and provide
> > pluggability to wire it up to android.util.Log on Android.
> >
> > - I think Matthias noticed that the API is nicely aligned with the
> > one for iOS, which is exactly what we want. (I didn't compare, so
> > I can't say :)
> >
> > - Looking at HttpRestProvider get() method doesn't take an id -
> > it's an operation that retrieves a list of all instances -
> > presuming URL points to something like http://host/task, and not
> > to something like http://host/task/100.
> > (https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/core/HttpRestProvider.java#L46)
> >
> > That makes sense in the HTTP meaning of GET.
>
>
> I'd use plural for endpoint URIs (e.g. /tasks and not /task)
>
> We need get() for retrieving _all_ items and get(id) for retrieving a
> single object (=> /tasks/{id}).
> (We also need 'queries', eventually)
>
> Regarding iOS... I left a TODO on the more high level 'filter' read
> (not implemented in the rest adapter)
> =>
> https://github.com/aerogear/aerogear-ios/blob/master/AeroGear-iOS/AeroGear-iOS/AGPipe.h#L55
>
> The 'filterObject' argument *could* be:
>  - an id to look up a single entity
>  - some more complex query object (e.g. a range or what not)
> ==> this all would translate in to a 'get()' invocation, where the
> method takes an argument.
>
> For looking up a single entity, of course I could have added a
> 'read(id)', which invokes an underlying 'http.get(id)'; Perhaps I add
> that read(id) now :)
>
> Anyways, back to Android... get() is fine for all, BUT get(id) is
> also needed.

I think the question here becomes from what point of view should we understand terms get(), put(), post() ...
These are clearly meant as HTTP spec terms, so at least inside this class we should understand them strictly from that POV. So I think in terms of API a better approach would be to have a URL represent a full URL endpoint (asset type + a potential id for an instance).

That way GET, POST, PUT, DELETE retain their well defined meaning.

A wrapper API around this can then have a more business interface understanding of it (persistence understanding in our case - with operation names like: create, update, findById, query, delete).

Or, we could add another series of methods called:

getChild(String id), deleteChild(String id), postChild(String id, String content), putChild(String id, String content).

But what do you do for entities that are properties of your child resources i.e. http://hostname/customers/100/orders?

I think it would be best to have another HttpRestAdapter with a new URL: http://hostname/customers/100/orders.

>
> >
> > It means, that if we want to get a single instance of a resource
> > (i.e. Task#100) we have to create a new HttpRestProvider with a
> > new URL.
> >
> > For delete(), OTOH we can execute it with a specific id, and the
> > same goes for put() to perform an update of a child resource.
>
>
> Correct, a put on /tasks does make no sense. A delete on /tasks would
> (I guess in most cases) mean all items are deleted, which feels
> wrong....
>
Sure, it's all relative to endpoint URL - to decide if it makes sense or not. But if I understand correctly the resource URL in our design should always be pointing to a resource type url (i.e. http://hostname/customers), and never to http://hostname/customers/100, or http://hostname/customers/100/orders), and that is the reason for current semantics of get(), delete(), post(), put() ?

> So IMO these put/delete operations make more sense on URIs, like
> /tasks/{id} (update / remove a single item).
>
>
>
> >
> > So we have GET, and POST operations performed on a 'parent'
> > resource, and DELETE, and PUT operations performed on child
> > resources. This asymmetry is confusing to me ... non-intuitive.
>
> Hrm... I disagree, as indicated in my sentences on put/delete
> above...
>
> I like Stefan's image on restful URIs:
> http://www.infoq.com/resource/articles/rest-introduction/en/resources/figure2.jpg
>

+1
Actually I'd say this exactly describes the API and usage as I proposed it above :)
URL defines a resource, and GET, DELETE, POST, PUT operate on that exact resource URL.
In this scheme of things you never operate on a 'child' resource, as the 'child' resource is already expressed through a URL (i.e. http://hostname/customers/100, or http://hostname/customers/100/orders - a collection on a child resource).

> The above picture 'models' URIs for this UML diagram:
> http://www.infoq.com/resource/articles/rest-introduction/en/resources/figure1.jpg
>

Actually I understand this as two layers where figure1 layer API, uses figure2 layer API.
That's the business interface layer that delegates to HttpRestAdapter, the two layer approach. In our case persistence view with operations like findById ...
 

> (Taken from this article =>
> http://www.infoq.com/articles/rest-introduction)
>
> However the figure2.jpg shows as well, that there maybe cases where a
> DELETE on /tasks (or '/customers/{id}/orders') and a POST on
> /tasks/{id} (or '/orders/{id}') can make sense.
>
> => We need to cover that too...
> If I recall correctly those two 'corner cases' are also not covered
> by
> the JavaScript library.
>
>
> > - I wouldn't eat exceptions, and return null even in a not-yet-real
> > exception handling :)
> > (https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/core/HttpRestProvider.java#L86)
> >
> > Really, either don't catch it, or if you do catch it, rethrow it as
> > is, or wrap into another - normally RuntimeException is perfectly
> > fine, so you don't pollute your API with throws declarations.
>
>
> +1 on this comment - but he added a TODO already ;)
>

That's why I wrote 'even in a not-yet-real exception handling' i.e. don't _ever_ do it like that :)

>
> >
> > - I would use IllegalArgumentException here:
> > https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/pipeline/AdapterFactory.java#L34
> >
> > It's more appropriate, as it's a standard pattern for this kind of
> > use-case. By convention UnsupportedOperationException is used for
> > empty methods where interface contract is not fully supported.
> >
>
> +1
>
>
>
> > - We have to do something about this "getId"
> > (https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/pipeline/RestAdapter.java#L76)
> >
> > One idea is to have an annotation - @Id. But scanning for
> > annotations needs to be done at init time or lazily on first use
> > ...
> > So maybe we could have an abstract base class with abstract method
> > getId() that every data object has to extend, and implement. A
> > simpler, and more robust solution actually, as compiler will
> > enforce it so there is no way for not providing one, as could
> > happen with forgotten or wrong placement of @Id annotation for
> > example.
> >
>
> I think the problem here is that not every object has an 'id', the
> field could be name 'recordId' - In JavaScript this is configurable.
> iOS has a TODO here...
>

That's interesting. Can you give some examples?


>
> -Matthias
>
>
>
> >
> > That's about it for now ... :)
> >
> > - marko
> >
> >
> > ----- Original Message -----
> >>
> >>
> >> Hi guys
> >>
> >>
> >> I did some changes in the android library based on iOS stuff, it's
> >> closer to the pipeline adapter implementation. I would appreciate
> >> feedback and review.
> >>
> >>
> >> https://github.com/aerogear/aerogear-android/pull/1
> >> https://github.com/aerogear/aerogear-android-todo/pull/1
> >>
> >>
> >> Thanks,
> >> Passos
> >> _______________________________________________
> >> aerogear-dev mailing list
> >> [hidden email]
> >> https://lists.jboss.org/mailman/listinfo/aerogear-dev
> >>
> > _______________________________________________
> > aerogear-dev mailing list
> > [hidden email]
> > https://lists.jboss.org/mailman/listinfo/aerogear-dev
>
>
>
> --
> Matthias Wessendorf
>
> blog: http://matthiaswessendorf.wordpress.com/
> sessions: http://www.slideshare.net/mwessendorf
> twitter: http://twitter.com/mwessendorf
>
> _______________________________________________
> aerogear-dev mailing list
> [hidden email]
> https://lists.jboss.org/mailman/listinfo/aerogear-dev
>
_______________________________________________
aerogear-dev mailing list
[hidden email]
https://lists.jboss.org/mailman/listinfo/aerogear-dev
Reply | Threaded
Open this post in threaded view
|

Re: [aerogear-dev] New Android Pipeline

Matthias Wessendorf
On Wed, Oct 3, 2012 at 3:00 PM, Marko Strukelj <[hidden email]> wrote:

>
>
>> Hello,
>>
>>
>> On Wed, Oct 3, 2012 at 12:37 AM, Marko Strukelj <[hidden email]>
>> wrote:
>> >
>> > Some comments ... (I haven't looked at this that closely before so
>> > there are some things here that relate to much earlier commits).
>> >
>> >
>> > - Logging seems to be the only android specific thing in the
>> > library ATM. I'm thinking that maybe we could treat it as a
>> > generic java client library using slf4j maybe, and provide
>> > pluggability to wire it up to android.util.Log on Android.
>> >
>> > - I think Matthias noticed that the API is nicely aligned with the
>> > one for iOS, which is exactly what we want. (I didn't compare, so
>> > I can't say :)
>> >
>> > - Looking at HttpRestProvider get() method doesn't take an id -
>> > it's an operation that retrieves a list of all instances -
>> > presuming URL points to something like http://host/task, and not
>> > to something like http://host/task/100.
>> > (https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/core/HttpRestProvider.java#L46)
>> >
>> > That makes sense in the HTTP meaning of GET.
>>
>>
>> I'd use plural for endpoint URIs (e.g. /tasks and not /task)
>>
>> We need get() for retrieving _all_ items and get(id) for retrieving a
>> single object (=> /tasks/{id}).
>> (We also need 'queries', eventually)
>>
>> Regarding iOS... I left a TODO on the more high level 'filter' read
>> (not implemented in the rest adapter)
>> =>
>> https://github.com/aerogear/aerogear-ios/blob/master/AeroGear-iOS/AeroGear-iOS/AGPipe.h#L55
>>
>> The 'filterObject' argument *could* be:
>>  - an id to look up a single entity
>>  - some more complex query object (e.g. a range or what not)
>> ==> this all would translate in to a 'get()' invocation, where the
>> method takes an argument.
>>
>> For looking up a single entity, of course I could have added a
>> 'read(id)', which invokes an underlying 'http.get(id)'; Perhaps I add
>> that read(id) now :)
>>
>> Anyways, back to Android... get() is fine for all, BUT get(id) is
>> also needed.
>
> I think the question here becomes from what point of view should we understand terms get(), put(), post() ...
> These are clearly meant as HTTP spec terms, so at least inside this class we should understand them strictly from that POV. So I think in terms of API a better approach would be to have a URL represent a full URL endpoint (asset type + a potential id for an instance).
>
> That way GET, POST, PUT, DELETE retain their well defined meaning.
>
> A wrapper API around this can then have a more business interface understanding of it (persistence understanding in our case - with operation names like: create, update, findById, query, delete).
>
> Or, we could add another series of methods called:
>
> getChild(String id), deleteChild(String id), postChild(String id, String content), putChild(String id, String content).
>
> But what do you do for entities that are properties of your child resources i.e. http://hostname/customers/100/orders?
>
> I think it would be best to have another HttpRestAdapter with a new URL: http://hostname/customers/100/orders.
>
>>
>> >
>> > It means, that if we want to get a single instance of a resource
>> > (i.e. Task#100) we have to create a new HttpRestProvider with a
>> > new URL.
>> >
>> > For delete(), OTOH we can execute it with a specific id, and the
>> > same goes for put() to perform an update of a child resource.
>>
>>
>> Correct, a put on /tasks does make no sense. A delete on /tasks would
>> (I guess in most cases) mean all items are deleted, which feels
>> wrong....
>>
> Sure, it's all relative to endpoint URL - to decide if it makes sense or not. But if I understand correctly the resource URL in our design should always be pointing to a resource type url (i.e. http://hostname/customers), and never to http://hostname/customers/100, or http://hostname/customers/100/orders), and that is the reason for current semantics of get(), delete(), post(), put() ?
>
>> So IMO these put/delete operations make more sense on URIs, like
>> /tasks/{id} (update / remove a single item).
>>
>>
>>
>> >
>> > So we have GET, and POST operations performed on a 'parent'
>> > resource, and DELETE, and PUT operations performed on child
>> > resources. This asymmetry is confusing to me ... non-intuitive.
>>
>> Hrm... I disagree, as indicated in my sentences on put/delete
>> above...
>>
>> I like Stefan's image on restful URIs:
>> http://www.infoq.com/resource/articles/rest-introduction/en/resources/figure2.jpg
>>
>
> +1
> Actually I'd say this exactly describes the API and usage as I proposed it above :)
> URL defines a resource, and GET, DELETE, POST, PUT operate on that exact resource URL.
> In this scheme of things you never operate on a 'child' resource, as the 'child' resource is already expressed through a URL (i.e. http://hostname/customers/100, or http://hostname/customers/100/orders - a collection on a child resource).


Not sure I follow your 'you never operate on a child resource' ... For
me the 'http://hostname/customers/100' is a child resource (-> member
of the 'customers' collection)... So a PUT (for updates) against that
'http://hostname/customers/100' URI, is IMO doing the work on the
child (member of the collection).... while, yes, the
'http://hostname/customers/100/orders' is a collection on a child
resource (=> the 'orders collection' of the customer #100)


>
>> The above picture 'models' URIs for this UML diagram:
>> http://www.infoq.com/resource/articles/rest-introduction/en/resources/figure1.jpg
>>
>
> Actually I understand this as two layers where figure1 layer API, uses figure2 layer API.
> That's the business interface layer that delegates to HttpRestAdapter, the two layer approach. In our case persistence view with operations like findById ...
>

yes figure1 is the 'business' API, and figure2 shows how these
'methods' map to the URIs...



>
>> (Taken from this article =>
>> http://www.infoq.com/articles/rest-introduction)
>>
>> However the figure2.jpg shows as well, that there maybe cases where a
>> DELETE on /tasks (or '/customers/{id}/orders') and a POST on
>> /tasks/{id} (or '/orders/{id}') can make sense.
>>
>> => We need to cover that too...
>> If I recall correctly those two 'corner cases' are also not covered
>> by
>> the JavaScript library.
>>
>>
>> > - I wouldn't eat exceptions, and return null even in a not-yet-real
>> > exception handling :)
>> > (https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/core/HttpRestProvider.java#L86)
>> >
>> > Really, either don't catch it, or if you do catch it, rethrow it as
>> > is, or wrap into another - normally RuntimeException is perfectly
>> > fine, so you don't pollute your API with throws declarations.
>>
>>
>> +1 on this comment - but he added a TODO already ;)
>>
>
> That's why I wrote 'even in a not-yet-real exception handling' i.e. don't _ever_ do it like that :)
>
>>
>> >
>> > - I would use IllegalArgumentException here:
>> > https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/pipeline/AdapterFactory.java#L34
>> >
>> > It's more appropriate, as it's a standard pattern for this kind of
>> > use-case. By convention UnsupportedOperationException is used for
>> > empty methods where interface contract is not fully supported.
>> >
>>
>> +1
>>
>>
>>
>> > - We have to do something about this "getId"
>> > (https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/pipeline/RestAdapter.java#L76)
>> >
>> > One idea is to have an annotation - @Id. But scanning for
>> > annotations needs to be done at init time or lazily on first use
>> > ...
>> > So maybe we could have an abstract base class with abstract method
>> > getId() that every data object has to extend, and implement. A
>> > simpler, and more robust solution actually, as compiler will
>> > enforce it so there is no way for not providing one, as could
>> > happen with forgotten or wrong placement of @Id annotation for
>> > example.
>> >
>>
>> I think the problem here is that not every object has an 'id', the
>> field could be name 'recordId' - In JavaScript this is configurable.
>> iOS has a TODO here...
>>
>
> That's interesting. Can you give some examples?
>
>
>>
>> -Matthias
>>
>>
>>
>> >
>> > That's about it for now ... :)
>> >
>> > - marko
>> >
>> >
>> > ----- Original Message -----
>> >>
>> >>
>> >> Hi guys
>> >>
>> >>
>> >> I did some changes in the android library based on iOS stuff, it's
>> >> closer to the pipeline adapter implementation. I would appreciate
>> >> feedback and review.
>> >>
>> >>
>> >> https://github.com/aerogear/aerogear-android/pull/1
>> >> https://github.com/aerogear/aerogear-android-todo/pull/1
>> >>
>> >>
>> >> Thanks,
>> >> Passos
>> >> _______________________________________________
>> >> aerogear-dev mailing list
>> >> [hidden email]
>> >> https://lists.jboss.org/mailman/listinfo/aerogear-dev
>> >>
>> > _______________________________________________
>> > aerogear-dev mailing list
>> > [hidden email]
>> > https://lists.jboss.org/mailman/listinfo/aerogear-dev
>>
>>
>>
>> --
>> Matthias Wessendorf
>>
>> blog: http://matthiaswessendorf.wordpress.com/
>> sessions: http://www.slideshare.net/mwessendorf
>> twitter: http://twitter.com/mwessendorf
>>
>> _______________________________________________
>> aerogear-dev mailing list
>> [hidden email]
>> https://lists.jboss.org/mailman/listinfo/aerogear-dev
>>
> _______________________________________________
> aerogear-dev mailing list
> [hidden email]
> https://lists.jboss.org/mailman/listinfo/aerogear-dev



--
Matthias Wessendorf

blog: http://matthiaswessendorf.wordpress.com/
sessions: http://www.slideshare.net/mwessendorf
twitter: http://twitter.com/mwessendorf

_______________________________________________
aerogear-dev mailing list
[hidden email]
https://lists.jboss.org/mailman/listinfo/aerogear-dev
Reply | Threaded
Open this post in threaded view
|

Re: [aerogear-dev] New Android Pipeline

Matthias Wessendorf
In reply to this post by Marko Strukelj
On Wed, Oct 3, 2012 at 2:05 PM, Marko Strukelj <[hidden email]> wrote:

>
>> On Wed, Oct 3, 2012 at 4:54 AM, Glen Daniels <[hidden email]>
>> wrote:
>> > Hi Passos,
>> >
>> > A few comments below.  I recognize that some of these things are
>> > derived
>> > from the JS implementation, which was the source for the iOS
>> > implementation - so some of these are questions about the the API
>> > in
>> > general as opposed to Android in particular.
>> >
>> > * I'm not sure I like creating an initial Pipe at Pipeline-creation
>> > time.  I guess this is meant as a shortcut, but in fact I think it
>> > confuses things.  Since we need a line of code to get the Pipe
>> > anyway,
>> > I'd rather it be consistent and look like this:
>> >
>> > Pipeline pipeline = new Pipeline(baseURL);
>> > Pipe<Task> tasks = pipeline.add("tasks", Task[].class);
>> > Pipe<Tag> tags = pipeline.add("tags", Tag[].class);
>> >
>> > ...rather than special-casing the first "tasks" pipe and using
>> > pipeline.get("tasks") to retrieve it.
>>
>> I think I can see room for improvement here too... At least on the
>> iOS/Android side of things...
>>
>> I guess the following 'scenario' is fine:
>>  => creating an empty pipeline with just the base URL, where adding
>> new pipes is only done with an 'add' method.
>>
>> > * Another consistency/symmetry issue (ah, I just read Marko's
>> > comments
>> > and I see he mentions the same thing): I think it's odd that get()
>> > and
>> > post() in HttpRestProvider can't take id arguments, and therefore
>> > must
>> > always work at the collection, as opposed to the individual
>> > resource
>> > level.  Simply adding overloads with an id argument would solve
>> > this I
>> > think, but maybe better to always require the id argument and allow
>> > null
>> > to access the collection.
>>
>> Right, the get(id) is needed and as seen in the references to Stefans
>> article (see previous email) a post(id) is also needed on the API, in
>> general.
>> (in the same way, how a DELETE on a 'collection' is needed => DELTE
>> on
>> /customers/{id}/orders cancels all orders for the customer)
>
> Do you mean to define DELETE operation as a 'cancel' (not actually as a 'remove' - more like an 'update' of existing entity)?
> Maybe that is a blackbox for this discussion, the important thing is that GET on collection returns empty after a successful DELETE operation.

the article is not clear on 'cancel', but I'd 'interpret' it as
'remove ALL orders from the customer...



> But still it is an interesting question to me ... what are best practices when it comes to mapping a RESTful view of an application to the actual model behind - the controller in-between the view and the model could map these GET, DELETE, PUT ... to some business logic operations - like 'cancel' the orders, as opposed to 'delete' them ...
>
>>
>> >
>> > * HttpRestProvider again: the post() method at least (and perhaps
>> > also
>> > put/delete) should probably return a value so we can get server
>> > data
>> > back to the caller if appropriate.
>> >
>> > * I'm not quite sure I understand why we need the HttpProvider
>> > interface
>> > at all.  What other implementations of this (that aren't
>> > HttpRestProvider) are there going to be?  We could get rid of it
>> > and
>> > just bake HTTP functionality into RestAdapter directly, no?
>>
>> I think this comes from the iOS layer...
>> I added a AGHttpClient, which is a very tiny subclass of
>> AFNetworking's HTTP client (recommended pattern by AFNetworking). The
>> subclass does (right now) not too much...
>>
>> So the 'restful adapter' basically delegates all HTTP calls to the
>> AGHttpClient:
>> https://github.com/aerogear/aerogear-ios/blob/master/AeroGear-iOS/AeroGear-iOS/AGRestAdapter.m
>>
>
> I asked myself the same question. Anyway it makes sense to make an API abstract in a way that allows plugging in a different implementation.
> It could maybe be HttpClient as opposed to SFTPClient? Or HttpRestClient as opposed to HttpSOAPClient?
>
>
>>
>> -Matthias
>>
>> > * We should discuss testing + mocking and write down some common
>> > tools +
>> > patterns.
>> >
>> > That's it for the moment...  I'm in Chicago on the way home to
>> > Boston
>> > right now, and will be tuned back in on Wednesday afternoon.  I'll
>> > try
>> > to write up some more Android thoughts at that point, specifically
>> > about
>> > the Android-specific surface area of AeroGear.
>> >
>> > Time to catch my last plane!
>> >
>> > Best,
>> > --Glen
>> >
>> > On 10/2/12 7:12 AM, Daniel Passos wrote:
>> >> Hi guys
>> >>
>> >> I did some changes in the android library based on iOS stuff, it's
>> >> closer to the pipeline adapter implementation. I would appreciate
>> >> feedback and review.
>> >>
>> >> https://github.com/aerogear/aerogear-android/pull/1
>> >> https://github.com/aerogear/aerogear-android-todo/pull/1
>> >>
>> >> Thanks,
>> >> Passos
>> >>
>> >>
>> >> _______________________________________________
>> >> aerogear-dev mailing list
>> >> [hidden email]
>> >> https://lists.jboss.org/mailman/listinfo/aerogear-dev
>> >>
>> >
>> > _______________________________________________
>> > aerogear-dev mailing list
>> > [hidden email]
>> > https://lists.jboss.org/mailman/listinfo/aerogear-dev
>>
>>
>>
>> --
>> Matthias Wessendorf
>>
>> blog: http://matthiaswessendorf.wordpress.com/
>> sessions: http://www.slideshare.net/mwessendorf
>> twitter: http://twitter.com/mwessendorf
>> _______________________________________________
>> aerogear-dev mailing list
>> [hidden email]
>> https://lists.jboss.org/mailman/listinfo/aerogear-dev
>>
> _______________________________________________
> aerogear-dev mailing list
> [hidden email]
> https://lists.jboss.org/mailman/listinfo/aerogear-dev



--
Matthias Wessendorf

blog: http://matthiaswessendorf.wordpress.com/
sessions: http://www.slideshare.net/mwessendorf
twitter: http://twitter.com/mwessendorf

_______________________________________________
aerogear-dev mailing list
[hidden email]
https://lists.jboss.org/mailman/listinfo/aerogear-dev
Reply | Threaded
Open this post in threaded view
|

Re: [aerogear-dev] New Android Pipeline

Marko Strukelj
In reply to this post by Matthias Wessendorf


> On Wed, Oct 3, 2012 at 3:00 PM, Marko Strukelj <[hidden email]>
> wrote:
> >
> >
> >> Hello,
> >>
> >>
> >> On Wed, Oct 3, 2012 at 12:37 AM, Marko Strukelj
> >> <[hidden email]>
> >> wrote:
> >> >
> >> > Some comments ... (I haven't looked at this that closely before
> >> > so
> >> > there are some things here that relate to much earlier commits).
> >> >
> >> >
> >> > - Logging seems to be the only android specific thing in the
> >> > library ATM. I'm thinking that maybe we could treat it as a
> >> > generic java client library using slf4j maybe, and provide
> >> > pluggability to wire it up to android.util.Log on Android.
> >> >
> >> > - I think Matthias noticed that the API is nicely aligned with
> >> > the
> >> > one for iOS, which is exactly what we want. (I didn't compare,
> >> > so
> >> > I can't say :)
> >> >
> >> > - Looking at HttpRestProvider get() method doesn't take an id -
> >> > it's an operation that retrieves a list of all instances -
> >> > presuming URL points to something like http://host/task, and not
> >> > to something like http://host/task/100.
> >> > (https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/core/HttpRestProvider.java#L46)
> >> >
> >> > That makes sense in the HTTP meaning of GET.
> >>
> >>
> >> I'd use plural for endpoint URIs (e.g. /tasks and not /task)
> >>
> >> We need get() for retrieving _all_ items and get(id) for
> >> retrieving a
> >> single object (=> /tasks/{id}).
> >> (We also need 'queries', eventually)
> >>
> >> Regarding iOS... I left a TODO on the more high level 'filter'
> >> read
> >> (not implemented in the rest adapter)
> >> =>
> >> https://github.com/aerogear/aerogear-ios/blob/master/AeroGear-iOS/AeroGear-iOS/AGPipe.h#L55
> >>
> >> The 'filterObject' argument *could* be:
> >>  - an id to look up a single entity
> >>  - some more complex query object (e.g. a range or what not)
> >> ==> this all would translate in to a 'get()' invocation, where the
> >> method takes an argument.
> >>
> >> For looking up a single entity, of course I could have added a
> >> 'read(id)', which invokes an underlying 'http.get(id)'; Perhaps I
> >> add
> >> that read(id) now :)
> >>
> >> Anyways, back to Android... get() is fine for all, BUT get(id) is
> >> also needed.
> >
> > I think the question here becomes from what point of view should we
> > understand terms get(), put(), post() ...
> > These are clearly meant as HTTP spec terms, so at least inside this
> > class we should understand them strictly from that POV. So I think
> > in terms of API a better approach would be to have a URL represent
> > a full URL endpoint (asset type + a potential id for an instance).
> >
> > That way GET, POST, PUT, DELETE retain their well defined meaning.
> >
> > A wrapper API around this can then have a more business interface
> > understanding of it (persistence understanding in our case - with
> > operation names like: create, update, findById, query, delete).
> >
> > Or, we could add another series of methods called:
> >
> > getChild(String id), deleteChild(String id), postChild(String id,
> > String content), putChild(String id, String content).
> >
> > But what do you do for entities that are properties of your child
> > resources i.e. http://hostname/customers/100/orders?
> >
> > I think it would be best to have another HttpRestAdapter with a new
> > URL: http://hostname/customers/100/orders.
> >
> >>
> >> >
> >> > It means, that if we want to get a single instance of a resource
> >> > (i.e. Task#100) we have to create a new HttpRestProvider with a
> >> > new URL.
> >> >
> >> > For delete(), OTOH we can execute it with a specific id, and the
> >> > same goes for put() to perform an update of a child resource.
> >>
> >>
> >> Correct, a put on /tasks does make no sense. A delete on /tasks
> >> would
> >> (I guess in most cases) mean all items are deleted, which feels
> >> wrong....
> >>
> > Sure, it's all relative to endpoint URL - to decide if it makes
> > sense or not. But if I understand correctly the resource URL in
> > our design should always be pointing to a resource type url (i.e.
> > http://hostname/customers), and never to
> > http://hostname/customers/100, or
> > http://hostname/customers/100/orders), and that is the reason for
> > current semantics of get(), delete(), post(), put() ?
> >
> >> So IMO these put/delete operations make more sense on URIs, like
> >> /tasks/{id} (update / remove a single item).
> >>
> >>
> >>
> >> >
> >> > So we have GET, and POST operations performed on a 'parent'
> >> > resource, and DELETE, and PUT operations performed on child
> >> > resources. This asymmetry is confusing to me ... non-intuitive.
> >>
> >> Hrm... I disagree, as indicated in my sentences on put/delete
> >> above...
> >>
> >> I like Stefan's image on restful URIs:
> >> http://www.infoq.com/resource/articles/rest-introduction/en/resources/figure2.jpg
> >>
> >
> > +1
> > Actually I'd say this exactly describes the API and usage as I
> > proposed it above :)
> > URL defines a resource, and GET, DELETE, POST, PUT operate on that
> > exact resource URL.
> > In this scheme of things you never operate on a 'child' resource,
> > as the 'child' resource is already expressed through a URL (i.e.
> > http://hostname/customers/100, or
> > http://hostname/customers/100/orders - a collection on a child
> > resource).
>
>
> Not sure I follow your 'you never operate on a child resource' ...
> For
> me the 'http://hostname/customers/100' is a child resource (-> member
> of the 'customers' collection)... So a PUT (for updates) against that
> 'http://hostname/customers/100' URI, is IMO doing the work on the
> child (member of the collection).... while, yes, the
> 'http://hostname/customers/100/orders' is a collection on a child
> resource (=> the 'orders collection' of the customer #100)
>

I mean in terms of HttpRestAdapter methods. One instance of HttpRestAdapter represents one URL which (I assert) ATM always maps to a resource-type URL - i.e. http://hostname/customers, not http://hostname/customers/100.

So on HttpRestAdapter("http://hostname/customers") operations operating on 'child' resources would be get(100), delete(100), put(100, data). While operations operating on a resource itself would be get(), and post().

That's why I propose a slightly different operation naming scheme: getChild(100), deleteChild(100), putChild(100, data). Even postChild(101, data) if that made some sense - id field in data would then have to be null for this to make sense. You still keep get() for list of all children, delete() for removal of all children (if we support a removal of a single child, then why not removal of all children?), post(data) for adding new child when and only when server is supposed to assign ID, while put(data) makes no sense.


>
> >
> >> The above picture 'models' URIs for this UML diagram:
> >> http://www.infoq.com/resource/articles/rest-introduction/en/resources/figure1.jpg
> >>
> >
> > Actually I understand this as two layers where figure1 layer API,
> > uses figure2 layer API.
> > That's the business interface layer that delegates to
> > HttpRestAdapter, the two layer approach. In our case persistence
> > view with operations like findById ...
> >
>
> yes figure1 is the 'business' API, and figure2 shows how these
> 'methods' map to the URIs...
>
>
>
> >
> >> (Taken from this article =>
> >> http://www.infoq.com/articles/rest-introduction)
> >>
> >> However the figure2.jpg shows as well, that there maybe cases
> >> where a
> >> DELETE on /tasks (or '/customers/{id}/orders') and a POST on
> >> /tasks/{id} (or '/orders/{id}') can make sense.
> >>
> >> => We need to cover that too...
> >> If I recall correctly those two 'corner cases' are also not
> >> covered
> >> by
> >> the JavaScript library.
> >>
> >>
> >> > - I wouldn't eat exceptions, and return null even in a
> >> > not-yet-real
> >> > exception handling :)
> >> > (https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/core/HttpRestProvider.java#L86)
> >> >
> >> > Really, either don't catch it, or if you do catch it, rethrow it
> >> > as
> >> > is, or wrap into another - normally RuntimeException is
> >> > perfectly
> >> > fine, so you don't pollute your API with throws declarations.
> >>
> >>
> >> +1 on this comment - but he added a TODO already ;)
> >>
> >
> > That's why I wrote 'even in a not-yet-real exception handling' i.e.
> > don't _ever_ do it like that :)
> >
> >>
> >> >
> >> > - I would use IllegalArgumentException here:
> >> > https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/pipeline/AdapterFactory.java#L34
> >> >
> >> > It's more appropriate, as it's a standard pattern for this kind
> >> > of
> >> > use-case. By convention UnsupportedOperationException is used
> >> > for
> >> > empty methods where interface contract is not fully supported.
> >> >
> >>
> >> +1
> >>
> >>
> >>
> >> > - We have to do something about this "getId"
> >> > (https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/pipeline/RestAdapter.java#L76)
> >> >
> >> > One idea is to have an annotation - @Id. But scanning for
> >> > annotations needs to be done at init time or lazily on first use
> >> > ...
> >> > So maybe we could have an abstract base class with abstract
> >> > method
> >> > getId() that every data object has to extend, and implement. A
> >> > simpler, and more robust solution actually, as compiler will
> >> > enforce it so there is no way for not providing one, as could
> >> > happen with forgotten or wrong placement of @Id annotation for
> >> > example.
> >> >
> >>
> >> I think the problem here is that not every object has an 'id', the
> >> field could be name 'recordId' - In JavaScript this is
> >> configurable.
> >> iOS has a TODO here...
> >>
> >
> > That's interesting. Can you give some examples?
> >
> >
> >>
> >> -Matthias
> >>
> >>
> >>
> >> >
> >> > That's about it for now ... :)
> >> >
> >> > - marko
> >> >
> >> >
> >> > ----- Original Message -----
> >> >>
> >> >>
> >> >> Hi guys
> >> >>
> >> >>
> >> >> I did some changes in the android library based on iOS stuff,
> >> >> it's
> >> >> closer to the pipeline adapter implementation. I would
> >> >> appreciate
> >> >> feedback and review.
> >> >>
> >> >>
> >> >> https://github.com/aerogear/aerogear-android/pull/1
> >> >> https://github.com/aerogear/aerogear-android-todo/pull/1
> >> >>
> >> >>
> >> >> Thanks,
> >> >> Passos
> >> >> _______________________________________________
> >> >> aerogear-dev mailing list
> >> >> [hidden email]
> >> >> https://lists.jboss.org/mailman/listinfo/aerogear-dev
> >> >>
> >> > _______________________________________________
> >> > aerogear-dev mailing list
> >> > [hidden email]
> >> > https://lists.jboss.org/mailman/listinfo/aerogear-dev
> >>
> >>
> >>
> >> --
> >> Matthias Wessendorf
> >>
> >> blog: http://matthiaswessendorf.wordpress.com/
> >> sessions: http://www.slideshare.net/mwessendorf
> >> twitter: http://twitter.com/mwessendorf
> >>
> >> _______________________________________________
> >> aerogear-dev mailing list
> >> [hidden email]
> >> https://lists.jboss.org/mailman/listinfo/aerogear-dev
> >>
> > _______________________________________________
> > aerogear-dev mailing list
> > [hidden email]
> > https://lists.jboss.org/mailman/listinfo/aerogear-dev
>
>
>
> --
> Matthias Wessendorf
>
> blog: http://matthiaswessendorf.wordpress.com/
> sessions: http://www.slideshare.net/mwessendorf
> twitter: http://twitter.com/mwessendorf
>
> _______________________________________________
> aerogear-dev mailing list
> [hidden email]
> https://lists.jboss.org/mailman/listinfo/aerogear-dev
>
_______________________________________________
aerogear-dev mailing list
[hidden email]
https://lists.jboss.org/mailman/listinfo/aerogear-dev
Reply | Threaded
Open this post in threaded view
|

Re: [aerogear-dev] New Android Pipeline

Matthias Wessendorf
On Wed, Oct 3, 2012 at 4:51 PM, Marko Strukelj <[hidden email]> wrote:

>
>
>> On Wed, Oct 3, 2012 at 3:00 PM, Marko Strukelj <[hidden email]>
>> wrote:
>> >
>> >
>> >> Hello,
>> >>
>> >>
>> >> On Wed, Oct 3, 2012 at 12:37 AM, Marko Strukelj
>> >> <[hidden email]>
>> >> wrote:
>> >> >
>> >> > Some comments ... (I haven't looked at this that closely before
>> >> > so
>> >> > there are some things here that relate to much earlier commits).
>> >> >
>> >> >
>> >> > - Logging seems to be the only android specific thing in the
>> >> > library ATM. I'm thinking that maybe we could treat it as a
>> >> > generic java client library using slf4j maybe, and provide
>> >> > pluggability to wire it up to android.util.Log on Android.
>> >> >
>> >> > - I think Matthias noticed that the API is nicely aligned with
>> >> > the
>> >> > one for iOS, which is exactly what we want. (I didn't compare,
>> >> > so
>> >> > I can't say :)
>> >> >
>> >> > - Looking at HttpRestProvider get() method doesn't take an id -
>> >> > it's an operation that retrieves a list of all instances -
>> >> > presuming URL points to something like http://host/task, and not
>> >> > to something like http://host/task/100.
>> >> > (https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/core/HttpRestProvider.java#L46)
>> >> >
>> >> > That makes sense in the HTTP meaning of GET.
>> >>
>> >>
>> >> I'd use plural for endpoint URIs (e.g. /tasks and not /task)
>> >>
>> >> We need get() for retrieving _all_ items and get(id) for
>> >> retrieving a
>> >> single object (=> /tasks/{id}).
>> >> (We also need 'queries', eventually)
>> >>
>> >> Regarding iOS... I left a TODO on the more high level 'filter'
>> >> read
>> >> (not implemented in the rest adapter)
>> >> =>
>> >> https://github.com/aerogear/aerogear-ios/blob/master/AeroGear-iOS/AeroGear-iOS/AGPipe.h#L55
>> >>
>> >> The 'filterObject' argument *could* be:
>> >>  - an id to look up a single entity
>> >>  - some more complex query object (e.g. a range or what not)
>> >> ==> this all would translate in to a 'get()' invocation, where the
>> >> method takes an argument.
>> >>
>> >> For looking up a single entity, of course I could have added a
>> >> 'read(id)', which invokes an underlying 'http.get(id)'; Perhaps I
>> >> add
>> >> that read(id) now :)
>> >>
>> >> Anyways, back to Android... get() is fine for all, BUT get(id) is
>> >> also needed.
>> >
>> > I think the question here becomes from what point of view should we
>> > understand terms get(), put(), post() ...
>> > These are clearly meant as HTTP spec terms, so at least inside this
>> > class we should understand them strictly from that POV. So I think
>> > in terms of API a better approach would be to have a URL represent
>> > a full URL endpoint (asset type + a potential id for an instance).
>> >
>> > That way GET, POST, PUT, DELETE retain their well defined meaning.
>> >
>> > A wrapper API around this can then have a more business interface
>> > understanding of it (persistence understanding in our case - with
>> > operation names like: create, update, findById, query, delete).
>> >
>> > Or, we could add another series of methods called:
>> >
>> > getChild(String id), deleteChild(String id), postChild(String id,
>> > String content), putChild(String id, String content).
>> >
>> > But what do you do for entities that are properties of your child
>> > resources i.e. http://hostname/customers/100/orders?
>> >
>> > I think it would be best to have another HttpRestAdapter with a new
>> > URL: http://hostname/customers/100/orders.
>> >
>> >>
>> >> >
>> >> > It means, that if we want to get a single instance of a resource
>> >> > (i.e. Task#100) we have to create a new HttpRestProvider with a
>> >> > new URL.
>> >> >
>> >> > For delete(), OTOH we can execute it with a specific id, and the
>> >> > same goes for put() to perform an update of a child resource.
>> >>
>> >>
>> >> Correct, a put on /tasks does make no sense. A delete on /tasks
>> >> would
>> >> (I guess in most cases) mean all items are deleted, which feels
>> >> wrong....
>> >>
>> > Sure, it's all relative to endpoint URL - to decide if it makes
>> > sense or not. But if I understand correctly the resource URL in
>> > our design should always be pointing to a resource type url (i.e.
>> > http://hostname/customers), and never to
>> > http://hostname/customers/100, or
>> > http://hostname/customers/100/orders), and that is the reason for
>> > current semantics of get(), delete(), post(), put() ?
>> >
>> >> So IMO these put/delete operations make more sense on URIs, like
>> >> /tasks/{id} (update / remove a single item).
>> >>
>> >>
>> >>
>> >> >
>> >> > So we have GET, and POST operations performed on a 'parent'
>> >> > resource, and DELETE, and PUT operations performed on child
>> >> > resources. This asymmetry is confusing to me ... non-intuitive.
>> >>
>> >> Hrm... I disagree, as indicated in my sentences on put/delete
>> >> above...
>> >>
>> >> I like Stefan's image on restful URIs:
>> >> http://www.infoq.com/resource/articles/rest-introduction/en/resources/figure2.jpg
>> >>
>> >
>> > +1
>> > Actually I'd say this exactly describes the API and usage as I
>> > proposed it above :)
>> > URL defines a resource, and GET, DELETE, POST, PUT operate on that
>> > exact resource URL.
>> > In this scheme of things you never operate on a 'child' resource,
>> > as the 'child' resource is already expressed through a URL (i.e.
>> > http://hostname/customers/100, or
>> > http://hostname/customers/100/orders - a collection on a child
>> > resource).
>>
>>
>> Not sure I follow your 'you never operate on a child resource' ...
>> For
>> me the 'http://hostname/customers/100' is a child resource (-> member
>> of the 'customers' collection)... So a PUT (for updates) against that
>> 'http://hostname/customers/100' URI, is IMO doing the work on the
>> child (member of the collection).... while, yes, the
>> 'http://hostname/customers/100/orders' is a collection on a child
>> resource (=> the 'orders collection' of the customer #100)
>>
>


Hello,

> I mean in terms of HttpRestAdapter methods. One instance of HttpRestAdapter represents one URL which (I assert) ATM always maps to a resource-type URL - i.e. http://hostname/customers, not http://hostname/customers/100.
>

The Pipe (public) API maps to to the 'customers' pipe, for instance:

Pipeline pipeline = new Pipeline("http://hostname");
Pipe<Customer> customersPipe = pipeline.add("customers", Customer[].class);

ATM, internally there is one 'http client' (e.g. HttpRestAdapter),
that operates (currently) on the 'http://hostname/customers' endpoint.

So it supports something like this:

// read ONE customer:
Customer c = customersPipe.read("100");

// give me ALL
List<Customer> allCustomers = customersPipe.read();

which means, currently the ONE 'http client' object (read:
HttpRestAdapter object) operates here on those 'two' URLs:
- http://hostname/customers (base url)
- http://hostname/customers/{id} (for a specific customer entity)



> So on HttpRestAdapter("http://hostname/customers") operations operating on 'child' resources would be get(100), delete(100), put(100, data). While operations operating on a resource itself would be get(), and post().
>
> That's why I propose a slightly different operation naming scheme: getChild(100), deleteChild(100), putChild(100, data). Even postChild(101, data) if that made some sense - id field in data would then have to be null for this to make sense. You still keep get() for list of all children, delete() for removal of all children (if we support a removal of a single child, then why not removal of all children?), post(data) for adding new child when and only when server is supposed to assign ID, while put(data) makes no sense.

Ah, so, internally ... a mapping like this would occur:


customersPipe.read("100") ==> restAdapter.getChild(givenId);
customersPipe.read();   ==> restAdapter.get();

But, that still IMO would be done with the one object (this.httpProvider)...


-Matthias


>
>
>>
>> >
>> >> The above picture 'models' URIs for this UML diagram:
>> >> http://www.infoq.com/resource/articles/rest-introduction/en/resources/figure1.jpg
>> >>
>> >
>> > Actually I understand this as two layers where figure1 layer API,
>> > uses figure2 layer API.
>> > That's the business interface layer that delegates to
>> > HttpRestAdapter, the two layer approach. In our case persistence
>> > view with operations like findById ...
>> >
>>
>> yes figure1 is the 'business' API, and figure2 shows how these
>> 'methods' map to the URIs...
>>
>>
>>
>> >
>> >> (Taken from this article =>
>> >> http://www.infoq.com/articles/rest-introduction)
>> >>
>> >> However the figure2.jpg shows as well, that there maybe cases
>> >> where a
>> >> DELETE on /tasks (or '/customers/{id}/orders') and a POST on
>> >> /tasks/{id} (or '/orders/{id}') can make sense.
>> >>
>> >> => We need to cover that too...
>> >> If I recall correctly those two 'corner cases' are also not
>> >> covered
>> >> by
>> >> the JavaScript library.
>> >>
>> >>
>> >> > - I wouldn't eat exceptions, and return null even in a
>> >> > not-yet-real
>> >> > exception handling :)
>> >> > (https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/core/HttpRestProvider.java#L86)
>> >> >
>> >> > Really, either don't catch it, or if you do catch it, rethrow it
>> >> > as
>> >> > is, or wrap into another - normally RuntimeException is
>> >> > perfectly
>> >> > fine, so you don't pollute your API with throws declarations.
>> >>
>> >>
>> >> +1 on this comment - but he added a TODO already ;)
>> >>
>> >
>> > That's why I wrote 'even in a not-yet-real exception handling' i.e.
>> > don't _ever_ do it like that :)
>> >
>> >>
>> >> >
>> >> > - I would use IllegalArgumentException here:
>> >> > https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/pipeline/AdapterFactory.java#L34
>> >> >
>> >> > It's more appropriate, as it's a standard pattern for this kind
>> >> > of
>> >> > use-case. By convention UnsupportedOperationException is used
>> >> > for
>> >> > empty methods where interface contract is not fully supported.
>> >> >
>> >>
>> >> +1
>> >>
>> >>
>> >>
>> >> > - We have to do something about this "getId"
>> >> > (https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/pipeline/RestAdapter.java#L76)
>> >> >
>> >> > One idea is to have an annotation - @Id. But scanning for
>> >> > annotations needs to be done at init time or lazily on first use
>> >> > ...
>> >> > So maybe we could have an abstract base class with abstract
>> >> > method
>> >> > getId() that every data object has to extend, and implement. A
>> >> > simpler, and more robust solution actually, as compiler will
>> >> > enforce it so there is no way for not providing one, as could
>> >> > happen with forgotten or wrong placement of @Id annotation for
>> >> > example.
>> >> >
>> >>
>> >> I think the problem here is that not every object has an 'id', the
>> >> field could be name 'recordId' - In JavaScript this is
>> >> configurable.
>> >> iOS has a TODO here...
>> >>
>> >
>> > That's interesting. Can you give some examples?
>> >
>> >
>> >>
>> >> -Matthias
>> >>
>> >>
>> >>
>> >> >
>> >> > That's about it for now ... :)
>> >> >
>> >> > - marko
>> >> >
>> >> >
>> >> > ----- Original Message -----
>> >> >>
>> >> >>
>> >> >> Hi guys
>> >> >>
>> >> >>
>> >> >> I did some changes in the android library based on iOS stuff,
>> >> >> it's
>> >> >> closer to the pipeline adapter implementation. I would
>> >> >> appreciate
>> >> >> feedback and review.
>> >> >>
>> >> >>
>> >> >> https://github.com/aerogear/aerogear-android/pull/1
>> >> >> https://github.com/aerogear/aerogear-android-todo/pull/1
>> >> >>
>> >> >>
>> >> >> Thanks,
>> >> >> Passos
>> >> >> _______________________________________________
>> >> >> aerogear-dev mailing list
>> >> >> [hidden email]
>> >> >> https://lists.jboss.org/mailman/listinfo/aerogear-dev
>> >> >>
>> >> > _______________________________________________
>> >> > aerogear-dev mailing list
>> >> > [hidden email]
>> >> > https://lists.jboss.org/mailman/listinfo/aerogear-dev
>> >>
>> >>
>> >>
>> >> --
>> >> Matthias Wessendorf
>> >>
>> >> blog: http://matthiaswessendorf.wordpress.com/
>> >> sessions: http://www.slideshare.net/mwessendorf
>> >> twitter: http://twitter.com/mwessendorf
>> >>
>> >> _______________________________________________
>> >> aerogear-dev mailing list
>> >> [hidden email]
>> >> https://lists.jboss.org/mailman/listinfo/aerogear-dev
>> >>
>> > _______________________________________________
>> > aerogear-dev mailing list
>> > [hidden email]
>> > https://lists.jboss.org/mailman/listinfo/aerogear-dev
>>
>>
>>
>> --
>> Matthias Wessendorf
>>
>> blog: http://matthiaswessendorf.wordpress.com/
>> sessions: http://www.slideshare.net/mwessendorf
>> twitter: http://twitter.com/mwessendorf
>>
>> _______________________________________________
>> aerogear-dev mailing list
>> [hidden email]
>> https://lists.jboss.org/mailman/listinfo/aerogear-dev
>>
> _______________________________________________
> aerogear-dev mailing list
> [hidden email]
> https://lists.jboss.org/mailman/listinfo/aerogear-dev



--
Matthias Wessendorf

blog: http://matthiaswessendorf.wordpress.com/
sessions: http://www.slideshare.net/mwessendorf
twitter: http://twitter.com/mwessendorf

_______________________________________________
aerogear-dev mailing list
[hidden email]
https://lists.jboss.org/mailman/listinfo/aerogear-dev
Reply | Threaded
Open this post in threaded view
|

Re: [aerogear-dev] New Android Pipeline

Matthias Wessendorf
In reply to this post by Marko Strukelj
Hello,

> But what do you do for entities that are properties of your child resources i.e. http://hostname/customers/100/orders?
>
> I think it would be best to have another HttpRestAdapter with a new URL: http://hostname/customers/100/orders.

For this example (customers and orders of a specific customer), I
_think_ you would model it with TWO pipes:

=== HARD CODED EXAMPLE ===

// base pipeline
Pipeline pipeline = new Pipeline("http://hostname/");

Pipe<Customer> customersPipe = pipeline.add("customers", Customer[].class);
==> maps to 'http://hostname/customers/'

Pipe<Order> ordersOfCustomer100 = pipeline.add("customers/100/orders",
Order[].class);
==> maps to 'http://hostname/customers/100/orders'

So here, technically in the implementation detail you would actually
have two _different_ HttpRestAdapter objects, one in the
'customersPipe' pipe and one in 'ordersOfCustomer100'.

Greetings,
Matthias

>
>>
>> >
>> > It means, that if we want to get a single instance of a resource
>> > (i.e. Task#100) we have to create a new HttpRestProvider with a
>> > new URL.
>> >
>> > For delete(), OTOH we can execute it with a specific id, and the
>> > same goes for put() to perform an update of a child resource.
>>
>>
>> Correct, a put on /tasks does make no sense. A delete on /tasks would
>> (I guess in most cases) mean all items are deleted, which feels
>> wrong....
>>
> Sure, it's all relative to endpoint URL - to decide if it makes sense or not. But if I understand correctly the resource URL in our design should always be pointing to a resource type url (i.e. http://hostname/customers), and never to http://hostname/customers/100, or http://hostname/customers/100/orders), and that is the reason for current semantics of get(), delete(), post(), put() ?
>
>> So IMO these put/delete operations make more sense on URIs, like
>> /tasks/{id} (update / remove a single item).
>>
>>
>>
>> >
>> > So we have GET, and POST operations performed on a 'parent'
>> > resource, and DELETE, and PUT operations performed on child
>> > resources. This asymmetry is confusing to me ... non-intuitive.
>>
>> Hrm... I disagree, as indicated in my sentences on put/delete
>> above...
>>
>> I like Stefan's image on restful URIs:
>> http://www.infoq.com/resource/articles/rest-introduction/en/resources/figure2.jpg
>>
>
> +1
> Actually I'd say this exactly describes the API and usage as I proposed it above :)
> URL defines a resource, and GET, DELETE, POST, PUT operate on that exact resource URL.
> In this scheme of things you never operate on a 'child' resource, as the 'child' resource is already expressed through a URL (i.e. http://hostname/customers/100, or http://hostname/customers/100/orders - a collection on a child resource).
>
>> The above picture 'models' URIs for this UML diagram:
>> http://www.infoq.com/resource/articles/rest-introduction/en/resources/figure1.jpg
>>
>
> Actually I understand this as two layers where figure1 layer API, uses figure2 layer API.
> That's the business interface layer that delegates to HttpRestAdapter, the two layer approach. In our case persistence view with operations like findById ...
>
>
>> (Taken from this article =>
>> http://www.infoq.com/articles/rest-introduction)
>>
>> However the figure2.jpg shows as well, that there maybe cases where a
>> DELETE on /tasks (or '/customers/{id}/orders') and a POST on
>> /tasks/{id} (or '/orders/{id}') can make sense.
>>
>> => We need to cover that too...
>> If I recall correctly those two 'corner cases' are also not covered
>> by
>> the JavaScript library.
>>
>>
>> > - I wouldn't eat exceptions, and return null even in a not-yet-real
>> > exception handling :)
>> > (https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/core/HttpRestProvider.java#L86)
>> >
>> > Really, either don't catch it, or if you do catch it, rethrow it as
>> > is, or wrap into another - normally RuntimeException is perfectly
>> > fine, so you don't pollute your API with throws declarations.
>>
>>
>> +1 on this comment - but he added a TODO already ;)
>>
>
> That's why I wrote 'even in a not-yet-real exception handling' i.e. don't _ever_ do it like that :)
>
>>
>> >
>> > - I would use IllegalArgumentException here:
>> > https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/pipeline/AdapterFactory.java#L34
>> >
>> > It's more appropriate, as it's a standard pattern for this kind of
>> > use-case. By convention UnsupportedOperationException is used for
>> > empty methods where interface contract is not fully supported.
>> >
>>
>> +1
>>
>>
>>
>> > - We have to do something about this "getId"
>> > (https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/pipeline/RestAdapter.java#L76)
>> >
>> > One idea is to have an annotation - @Id. But scanning for
>> > annotations needs to be done at init time or lazily on first use
>> > ...
>> > So maybe we could have an abstract base class with abstract method
>> > getId() that every data object has to extend, and implement. A
>> > simpler, and more robust solution actually, as compiler will
>> > enforce it so there is no way for not providing one, as could
>> > happen with forgotten or wrong placement of @Id annotation for
>> > example.
>> >
>>
>> I think the problem here is that not every object has an 'id', the
>> field could be name 'recordId' - In JavaScript this is configurable.
>> iOS has a TODO here...
>>
>
> That's interesting. Can you give some examples?
>
>
>>
>> -Matthias
>>
>>
>>
>> >
>> > That's about it for now ... :)
>> >
>> > - marko
>> >
>> >
>> > ----- Original Message -----
>> >>
>> >>
>> >> Hi guys
>> >>
>> >>
>> >> I did some changes in the android library based on iOS stuff, it's
>> >> closer to the pipeline adapter implementation. I would appreciate
>> >> feedback and review.
>> >>
>> >>
>> >> https://github.com/aerogear/aerogear-android/pull/1
>> >> https://github.com/aerogear/aerogear-android-todo/pull/1
>> >>
>> >>
>> >> Thanks,
>> >> Passos
>> >> _______________________________________________
>> >> aerogear-dev mailing list
>> >> [hidden email]
>> >> https://lists.jboss.org/mailman/listinfo/aerogear-dev
>> >>
>> > _______________________________________________
>> > aerogear-dev mailing list
>> > [hidden email]
>> > https://lists.jboss.org/mailman/listinfo/aerogear-dev
>>
>>
>>
>> --
>> Matthias Wessendorf
>>
>> blog: http://matthiaswessendorf.wordpress.com/
>> sessions: http://www.slideshare.net/mwessendorf
>> twitter: http://twitter.com/mwessendorf
>>
>> _______________________________________________
>> aerogear-dev mailing list
>> [hidden email]
>> https://lists.jboss.org/mailman/listinfo/aerogear-dev
>>
> _______________________________________________
> aerogear-dev mailing list
> [hidden email]
> https://lists.jboss.org/mailman/listinfo/aerogear-dev



--
Matthias Wessendorf

blog: http://matthiaswessendorf.wordpress.com/
sessions: http://www.slideshare.net/mwessendorf
twitter: http://twitter.com/mwessendorf

_______________________________________________
aerogear-dev mailing list
[hidden email]
https://lists.jboss.org/mailman/listinfo/aerogear-dev
Reply | Threaded
Open this post in threaded view
|

Re: [aerogear-dev] New Android Pipeline

Matthias Wessendorf
> === HARD CODED EXAMPLE ===

> Pipeline pipeline = new Pipeline("http://hostname/");
> Pipe<Customer> customersPipe = pipeline.add("customers", Customer[].class);
> Pipe<Order> ordersOfCustomer100 = pipeline.add("customers/100/orders",
.....

I _only_ think that the URL argument reads odd :-)

It would be more clear without a baseURL on the Pipeline.... hrm...

but on the other hand, that's why we have bseURL and endpoint on the
'add pipe' API...


-M


>
>>
>>>
>>> >
>>> > It means, that if we want to get a single instance of a resource
>>> > (i.e. Task#100) we have to create a new HttpRestProvider with a
>>> > new URL.
>>> >
>>> > For delete(), OTOH we can execute it with a specific id, and the
>>> > same goes for put() to perform an update of a child resource.
>>>
>>>
>>> Correct, a put on /tasks does make no sense. A delete on /tasks would
>>> (I guess in most cases) mean all items are deleted, which feels
>>> wrong....
>>>
>> Sure, it's all relative to endpoint URL - to decide if it makes sense or not. But if I understand correctly the resource URL in our design should always be pointing to a resource type url (i.e. http://hostname/customers), and never to http://hostname/customers/100, or http://hostname/customers/100/orders), and that is the reason for current semantics of get(), delete(), post(), put() ?
>>
>>> So IMO these put/delete operations make more sense on URIs, like
>>> /tasks/{id} (update / remove a single item).
>>>
>>>
>>>
>>> >
>>> > So we have GET, and POST operations performed on a 'parent'
>>> > resource, and DELETE, and PUT operations performed on child
>>> > resources. This asymmetry is confusing to me ... non-intuitive.
>>>
>>> Hrm... I disagree, as indicated in my sentences on put/delete
>>> above...
>>>
>>> I like Stefan's image on restful URIs:
>>> http://www.infoq.com/resource/articles/rest-introduction/en/resources/figure2.jpg
>>>
>>
>> +1
>> Actually I'd say this exactly describes the API and usage as I proposed it above :)
>> URL defines a resource, and GET, DELETE, POST, PUT operate on that exact resource URL.
>> In this scheme of things you never operate on a 'child' resource, as the 'child' resource is already expressed through a URL (i.e. http://hostname/customers/100, or http://hostname/customers/100/orders - a collection on a child resource).
>>
>>> The above picture 'models' URIs for this UML diagram:
>>> http://www.infoq.com/resource/articles/rest-introduction/en/resources/figure1.jpg
>>>
>>
>> Actually I understand this as two layers where figure1 layer API, uses figure2 layer API.
>> That's the business interface layer that delegates to HttpRestAdapter, the two layer approach. In our case persistence view with operations like findById ...
>>
>>
>>> (Taken from this article =>
>>> http://www.infoq.com/articles/rest-introduction)
>>>
>>> However the figure2.jpg shows as well, that there maybe cases where a
>>> DELETE on /tasks (or '/customers/{id}/orders') and a POST on
>>> /tasks/{id} (or '/orders/{id}') can make sense.
>>>
>>> => We need to cover that too...
>>> If I recall correctly those two 'corner cases' are also not covered
>>> by
>>> the JavaScript library.
>>>
>>>
>>> > - I wouldn't eat exceptions, and return null even in a not-yet-real
>>> > exception handling :)
>>> > (https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/core/HttpRestProvider.java#L86)
>>> >
>>> > Really, either don't catch it, or if you do catch it, rethrow it as
>>> > is, or wrap into another - normally RuntimeException is perfectly
>>> > fine, so you don't pollute your API with throws declarations.
>>>
>>>
>>> +1 on this comment - but he added a TODO already ;)
>>>
>>
>> That's why I wrote 'even in a not-yet-real exception handling' i.e. don't _ever_ do it like that :)
>>
>>>
>>> >
>>> > - I would use IllegalArgumentException here:
>>> > https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/pipeline/AdapterFactory.java#L34
>>> >
>>> > It's more appropriate, as it's a standard pattern for this kind of
>>> > use-case. By convention UnsupportedOperationException is used for
>>> > empty methods where interface contract is not fully supported.
>>> >
>>>
>>> +1
>>>
>>>
>>>
>>> > - We have to do something about this "getId"
>>> > (https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/pipeline/RestAdapter.java#L76)
>>> >
>>> > One idea is to have an annotation - @Id. But scanning for
>>> > annotations needs to be done at init time or lazily on first use
>>> > ...
>>> > So maybe we could have an abstract base class with abstract method
>>> > getId() that every data object has to extend, and implement. A
>>> > simpler, and more robust solution actually, as compiler will
>>> > enforce it so there is no way for not providing one, as could
>>> > happen with forgotten or wrong placement of @Id annotation for
>>> > example.
>>> >
>>>
>>> I think the problem here is that not every object has an 'id', the
>>> field could be name 'recordId' - In JavaScript this is configurable.
>>> iOS has a TODO here...
>>>
>>
>> That's interesting. Can you give some examples?
>>
>>
>>>
>>> -Matthias
>>>
>>>
>>>
>>> >
>>> > That's about it for now ... :)
>>> >
>>> > - marko
>>> >
>>> >
>>> > ----- Original Message -----
>>> >>
>>> >>
>>> >> Hi guys
>>> >>
>>> >>
>>> >> I did some changes in the android library based on iOS stuff, it's
>>> >> closer to the pipeline adapter implementation. I would appreciate
>>> >> feedback and review.
>>> >>
>>> >>
>>> >> https://github.com/aerogear/aerogear-android/pull/1
>>> >> https://github.com/aerogear/aerogear-android-todo/pull/1
>>> >>
>>> >>
>>> >> Thanks,
>>> >> Passos
>>> >> _______________________________________________
>>> >> aerogear-dev mailing list
>>> >> [hidden email]
>>> >> https://lists.jboss.org/mailman/listinfo/aerogear-dev
>>> >>
>>> > _______________________________________________
>>> > aerogear-dev mailing list
>>> > [hidden email]
>>> > https://lists.jboss.org/mailman/listinfo/aerogear-dev
>>>
>>>
>>>
>>> --
>>> Matthias Wessendorf
>>>
>>> blog: http://matthiaswessendorf.wordpress.com/
>>> sessions: http://www.slideshare.net/mwessendorf
>>> twitter: http://twitter.com/mwessendorf
>>>
>>> _______________________________________________
>>> aerogear-dev mailing list
>>> [hidden email]
>>> https://lists.jboss.org/mailman/listinfo/aerogear-dev
>>>
>> _______________________________________________
>> aerogear-dev mailing list
>> [hidden email]
>> https://lists.jboss.org/mailman/listinfo/aerogear-dev
>
>
>
> --
> Matthias Wessendorf
>
> blog: http://matthiaswessendorf.wordpress.com/
> sessions: http://www.slideshare.net/mwessendorf
> twitter: http://twitter.com/mwessendorf



--
Matthias Wessendorf

blog: http://matthiaswessendorf.wordpress.com/
sessions: http://www.slideshare.net/mwessendorf
twitter: http://twitter.com/mwessendorf

_______________________________________________
aerogear-dev mailing list
[hidden email]
https://lists.jboss.org/mailman/listinfo/aerogear-dev
Reply | Threaded
Open this post in threaded view
|

Re: [aerogear-dev] New Android Pipeline

Kris Borchers
In reply to this post by Matthias Wessendorf

On Oct 3, 2012, at 10:16 AM, Matthias Wessendorf <[hidden email]> wrote:

> Hello,
>
>> But what do you do for entities that are properties of your child resources i.e. http://hostname/customers/100/orders?
>>
>> I think it would be best to have another HttpRestAdapter with a new URL: http://hostname/customers/100/orders.
>
> For this example (customers and orders of a specific customer), I
> _think_ you would model it with TWO pipes:
>
> === HARD CODED EXAMPLE ===
>
> // base pipeline
> Pipeline pipeline = new Pipeline("http://hostname/");
>
> Pipe<Customer> customersPipe = pipeline.add("customers", Customer[].class);
> ==> maps to 'http://hostname/customers/'
>
> Pipe<Order> ordersOfCustomer100 = pipeline.add("customers/100/orders",
> Order[].class);
> ==> maps to 'http://hostname/customers/100/orders'

This would not work because you would have a / in the name. You would want to set "customers/100/orders" as the endpoint and give the pipe some name like cust100Orders

>
> So here, technically in the implementation detail you would actually
> have two _different_ HttpRestAdapter objects, one in the
> 'customersPipe' pipe and one in 'ordersOfCustomer100'.
>
> Greetings,
> Matthias
>
>>
>>>
>>>>
>>>> It means, that if we want to get a single instance of a resource
>>>> (i.e. Task#100) we have to create a new HttpRestProvider with a
>>>> new URL.
>>>>
>>>> For delete(), OTOH we can execute it with a specific id, and the
>>>> same goes for put() to perform an update of a child resource.
>>>
>>>
>>> Correct, a put on /tasks does make no sense. A delete on /tasks would
>>> (I guess in most cases) mean all items are deleted, which feels
>>> wrong....
>>>
>> Sure, it's all relative to endpoint URL - to decide if it makes sense or not. But if I understand correctly the resource URL in our design should always be pointing to a resource type url (i.e. http://hostname/customers), and never to http://hostname/customers/100, or http://hostname/customers/100/orders), and that is the reason for current semantics of get(), delete(), post(), put() ?
>>
>>> So IMO these put/delete operations make more sense on URIs, like
>>> /tasks/{id} (update / remove a single item).
>>>
>>>
>>>
>>>>
>>>> So we have GET, and POST operations performed on a 'parent'
>>>> resource, and DELETE, and PUT operations performed on child
>>>> resources. This asymmetry is confusing to me ... non-intuitive.
>>>
>>> Hrm... I disagree, as indicated in my sentences on put/delete
>>> above...
>>>
>>> I like Stefan's image on restful URIs:
>>> http://www.infoq.com/resource/articles/rest-introduction/en/resources/figure2.jpg
>>>
>>
>> +1
>> Actually I'd say this exactly describes the API and usage as I proposed it above :)
>> URL defines a resource, and GET, DELETE, POST, PUT operate on that exact resource URL.
>> In this scheme of things you never operate on a 'child' resource, as the 'child' resource is already expressed through a URL (i.e. http://hostname/customers/100, or http://hostname/customers/100/orders - a collection on a child resource).
>>
>>> The above picture 'models' URIs for this UML diagram:
>>> http://www.infoq.com/resource/articles/rest-introduction/en/resources/figure1.jpg
>>>
>>
>> Actually I understand this as two layers where figure1 layer API, uses figure2 layer API.
>> That's the business interface layer that delegates to HttpRestAdapter, the two layer approach. In our case persistence view with operations like findById ...
>>
>>
>>> (Taken from this article =>
>>> http://www.infoq.com/articles/rest-introduction)
>>>
>>> However the figure2.jpg shows as well, that there maybe cases where a
>>> DELETE on /tasks (or '/customers/{id}/orders') and a POST on
>>> /tasks/{id} (or '/orders/{id}') can make sense.
>>>
>>> => We need to cover that too...
>>> If I recall correctly those two 'corner cases' are also not covered
>>> by
>>> the JavaScript library.
>>>
>>>
>>>> - I wouldn't eat exceptions, and return null even in a not-yet-real
>>>> exception handling :)
>>>> (https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/core/HttpRestProvider.java#L86)
>>>>
>>>> Really, either don't catch it, or if you do catch it, rethrow it as
>>>> is, or wrap into another - normally RuntimeException is perfectly
>>>> fine, so you don't pollute your API with throws declarations.
>>>
>>>
>>> +1 on this comment - but he added a TODO already ;)
>>>
>>
>> That's why I wrote 'even in a not-yet-real exception handling' i.e. don't _ever_ do it like that :)
>>
>>>
>>>>
>>>> - I would use IllegalArgumentException here:
>>>> https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/pipeline/AdapterFactory.java#L34
>>>>
>>>> It's more appropriate, as it's a standard pattern for this kind of
>>>> use-case. By convention UnsupportedOperationException is used for
>>>> empty methods where interface contract is not fully supported.
>>>>
>>>
>>> +1
>>>
>>>
>>>
>>>> - We have to do something about this "getId"
>>>> (https://github.com/danielpassos/aerogear-android/blob/new-pipeline/src/main/java/org/aerogear/android/pipeline/RestAdapter.java#L76)
>>>>
>>>> One idea is to have an annotation - @Id. But scanning for
>>>> annotations needs to be done at init time or lazily on first use
>>>> ...
>>>> So maybe we could have an abstract base class with abstract method
>>>> getId() that every data object has to extend, and implement. A
>>>> simpler, and more robust solution actually, as compiler will
>>>> enforce it so there is no way for not providing one, as could
>>>> happen with forgotten or wrong placement of @Id annotation for
>>>> example.
>>>>
>>>
>>> I think the problem here is that not every object has an 'id', the
>>> field could be name 'recordId' - In JavaScript this is configurable.
>>> iOS has a TODO here...
>>>
>>
>> That's interesting. Can you give some examples?
>>
>>
>>>
>>> -Matthias
>>>
>>>
>>>
>>>>
>>>> That's about it for now ... :)
>>>>
>>>> - marko
>>>>
>>>>
>>>> ----- Original Message -----
>>>>>
>>>>>
>>>>> Hi guys
>>>>>
>>>>>
>>>>> I did some changes in the android library based on iOS stuff, it's
>>>>> closer to the pipeline adapter implementation. I would appreciate
>>>>> feedback and review.
>>>>>
>>>>>
>>>>> https://github.com/aerogear/aerogear-android/pull/1
>>>>> https://github.com/aerogear/aerogear-android-todo/pull/1
>>>>>
>>>>>
>>>>> Thanks,
>>>>> Passos
>>>>> _______________________________________________
>>>>> aerogear-dev mailing list
>>>>> [hidden email]
>>>>> https://lists.jboss.org/mailman/listinfo/aerogear-dev
>>>>>
>>>> _______________________________________________
>>>> aerogear-dev mailing list
>>>> [hidden email]
>>>> https://lists.jboss.org/mailman/listinfo/aerogear-dev
>>>
>>>
>>>
>>> --
>>> Matthias Wessendorf
>>>
>>> blog: http://matthiaswessendorf.wordpress.com/
>>> sessions: http://www.slideshare.net/mwessendorf
>>> twitter: http://twitter.com/mwessendorf
>>>
>>> _______________________________________________
>>> aerogear-dev mailing list
>>> [hidden email]
>>> https://lists.jboss.org/mailman/listinfo/aerogear-dev
>>>
>> _______________________________________________
>> aerogear-dev mailing list
>> [hidden email]
>> https://lists.jboss.org/mailman/listinfo/aerogear-dev
>
>
>
> --
> Matthias Wessendorf
>
> blog: http://matthiaswessendorf.wordpress.com/
> sessions: http://www.slideshare.net/mwessendorf
> twitter: http://twitter.com/mwessendorf
>
> _______________________________________________
> aerogear-dev mailing list
> [hidden email]
> https://lists.jboss.org/mailman/listinfo/aerogear-dev


_______________________________________________
aerogear-dev mailing list
[hidden email]
https://lists.jboss.org/mailman/listinfo/aerogear-dev
12