rave-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Erin Noe-Payne <erin.noe.pa...@gmail.com>
Subject Re: [Proposal] REST API interface
Date Fri, 19 Jul 2013 18:11:26 GMT
On Fri, Jul 19, 2013 at 1:24 PM, Chris Geer <chris@cxtsoftware.com> wrote:
> In the xml file you need to create the bean, then reference it in the
> server element near the top. Other than that...no, that should be all. I
> assume you set the Path attribute on the resource.
>
I did. I'm also messing around with the service injection, which may
be the issue. Haven't gotten it to work yet though.

> I thought we were going to do pages/<id>/regions/<id>/regionwidgets/<id>
> since it makes no sense to manage a region widget outside a region outside
> a page?
 Possibly. Right now I'm just trying to do a proof of concept with the
wrapped json object so I picked something simple with the service and
rest models already in place.

In general though I don't see any value to dealing with region widgets
as a nested resource (pages/:id/regions/:id...) over just dealing with
them directly. It's just adding weight to the pages controller, rather
than breaking them up and dealing with resource concerns separately.

I get what you're saying about regions and regionwidgets only making
sense in the context of a page, but you could say the same thing for
any 1-many associated resource. Both entities are always uniquely
identified, so why not deal with them individually? I see an upside of
simpler code, consistent api endpoints, and I see no downside.

>
>
> On Fri, Jul 19, 2013 at 10:16 AM, Erin Noe-Payne
> <erin.noe.payne@gmail.com>wrote:
>
>> I'm trying to register a new endpoint for regionWidgets. I've added
>> the interface and default implementation, and created / registered the
>> bean in cxf-applicationContext.xml.
>>
>> However, when I hit the endpoint I get an error:
>> [INFO] [talledLocalContainer] WARN :
>> org.apache.cxf.jaxrs.utils.JAXRSUtils - No operation matching request
>> path "/portal/api/rest/regionWidgets/1" is found, Relative Path: /1,
>> HTTP Method: GET, ContentType: */*, Accept:
>> text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,.
>> Please enable FINE/TRACE log level for more details.
>>
>> Is there anything else I need to do in order to create and register a
>> new endpoint?
>>
>> On Tue, Jul 16, 2013 at 11:53 PM, Erin Noe-Payne
>> <erin.noe.payne@gmail.com> wrote:
>> > On Tue, Jul 16, 2013 at 10:24 PM, Chris Geer <chris@cxtsoftware.com>
>> wrote:
>> >> On Tue, Jul 16, 2013 at 7:04 PM, Erin Noe-Payne <
>> erin.noe.payne@gmail.com>wrote:
>> >>
>> >>> On Tue, Jul 16, 2013 at 9:20 PM, Matt Franklin <
>> m.ben.franklin@gmail.com>
>> >>> wrote:
>> >>> > On Tue, Jul 16, 2013 at 12:53 PM, Chris Geer <chris@cxtsoftware.com>
>> >>> wrote:
>> >>> >
>> >>> >> On Tue, Jul 16, 2013 at 10:32 AM, Erin Noe-Payne
>> >>> >> <erin.noe.payne@gmail.com>wrote:
>> >>> >>
>> >>> >> > Any further discussion here? I would like to start implementing
>> more
>> >>> >> > of the REST APIs, as it is foundational for the entire
angular
>> >>> >> > architecture.
>> >>> >> >
>> >>> >> > My understanding from Matt is that the current apis in
trunk are
>> >>> >> > mostly proof of concept - they are not tested and much
of the
>> >>> >> > functionality is just stubbed. Are any of the rest api
>> implementations
>> >>> >> > in the code base a good working example? Is there other
>> documentation
>> >>> >> > we can reference?
>> >>> >> >
>> >>> >>
>> >>> >> I've been working on the People resource as a "reference" of
how I'd
>> >>> like
>> >>> >> to see them done but it's still a work in progress. I need
to go
>> back
>> >>> and
>> >>> >> pull out the JSONView stuff and reimplement the "fields" concept.
>> >>> Couple of
>> >>> >> notes:
>> >>> >>
>> >>> >>  - Object representations should be as flat as possible
>> >>> >> and separate requests should be made to nested resources to
get
>> nested
>> >>> >> details (i.e. if you have regions and regions/1/regionwidgets,
the
>> >>> regions
>> >>> >> representation should not contain an array of regionwidgets)
>> >>> >>
>> >>> >
>> >>> > I am concerned about the round trips to support this when rendering
>> the
>> >>> > page.  With any page that has a sufficient number of gadgets, adding
>> to
>> >>> the
>> >>> > number of requests becomes problematic.
>> >>> >
>> >>>
>> >>> I see that rule applying to the "standard" rest endpoints for crud
>> >>> operations on resources. We will have some number of special endpoints
>> >>> to support frequently used operations of clients. The major example
>> >>> there is the page / pages for render endpoint, which will include the
>> >>> nested regions, regionwidgets, and their rpc tokens, etc.
>> >>>
>> >>
>> >> +1
>> >
>> > So my thought is that we have the standard crud endpoints for all
>> > individual resources. For frequently-used read operations in which we
>> > need composite data sets we will have a small number of custom
>> > endpoints to serve that data. Those are read-only endpoints, they will
>> > not support create / update / delete operations.
>> >
>> > At a time when we need one of our composite data views, such as on a
>> > page load, the client will make a request and get the composite data -
>> > page, regions, regionwidgets and so on. The client can decompose those
>> > into the individual component resources which have corresponding
>> > angular $resource services, and which talk to the standard endpoints
>> > to support further crud operations on them.
>> >
>> > Make sense / thoughts?
>> >
>> >>
>> >>>
>> >>> >
>> >>> >>  - All methods should return standard HTTP codes. We should
document
>> >>> this
>> >>> >> further on the wiki to make sure we all do the same way.
>> >>> >>  - We won't accept partial updates with PUT, we will eventually
add
>> >>> PATCH
>> >>> >> to support that in the future
>> >>> >>  - If the "fields" query attribute isn't included in a GET
then all
>> >>> fields
>> >>> >> are returned.
>> >>> >>  - What is the full meta structure we want to return?
>> >>> >>
>> >>> >>
>> >>> >> >
>> >>> >> > On Thu, Jul 11, 2013 at 5:48 PM, Erin Noe-Payne
>> >>> >> > <erin.noe.payne@gmail.com> wrote:
>> >>> >> > > On Thu, Jul 11, 2013 at 5:02 PM, Matt Franklin <
>> >>> >> m.ben.franklin@gmail.com>
>> >>> >> > wrote:
>> >>> >> > >> +1 for every one of Chris' +1s, unless otherwise
noted.
>> >>> >> > >>
>> >>> >> > >> On Thu, Jul 11, 2013 at 3:47 PM, Chris Geer <
>> chris@cxtsoftware.com
>> >>> >
>> >>> >> > wrote:
>> >>> >> > >>
>> >>> >> > >>> Oh boy!! :)
>> >>> >> > >>>
>> >>> >> > >>> Comments inline
>> >>> >> > >>>
>> >>> >> > >>>
>> >>> >> > >>> On Thu, Jul 11, 2013 at 1:20 PM, Erin Noe-Payne
<
>> >>> >> > erin.noe.payne@gmail.com
>> >>> >> > >>> >wrote:
>> >>> >> > >>>
>> >>> >> > >>> > Hey All,
>> >>> >> > >>> >
>> >>> >> > >>> > As we are starting to look at the rest
apis in more detail,
>> I
>> >>> would
>> >>> >> > >>> > like to discuss and agree upon a consistent
interface for
>> our
>> >>> apis.
>> >>> >> > >>> > We currently have several developers
interested in
>> contributing
>> >>> to
>> >>> >> > the
>> >>> >> > >>> > apis and the angular branch, and I would
like to solidify
>> the
>> >>> >> > >>> > interface, methods, response format,
etc so that we can be
>> on
>> >>> the
>> >>> >> > same
>> >>> >> > >>> > page going forward. If we can agree
on an api virtualization
>> >>> layer
>> >>> >> > >>> > then we should be able to build against
it on the server
>> and on
>> >>> the
>> >>> >> > >>> > angular application in parallel.
>> >>> >> > >>> >
>> >>> >> > >>> > I'll start with a proposal and look
for feedback to iterate
>> from
>> >>> >> > there.
>> >>> >> > >>> >
>> >>> >> > >>> > 1. API root url
>> >>> >> > >>> >
>> >>> >> > >>> > "/api". Drop support for rpc api, move
from /api/rest to
>> just
>> >>> /api.
>> >>> >> > >>> >
>> >>> >> > >>>
>> >>> >> > >>> +1 - the only downside of this is that it
prohibits
>> implementing
>> >>> over
>> >>> >> > time
>> >>> >> > >>> and requires a rip/replace approach of the
whole system
>> >>> >> > >
>> >>> >> > > Well the development in trunk can continue to happen
on /rest.
>> >>> Angular
>> >>> >> > > (aka the consuming client for most of these apis)
is already
>> >>> happening
>> >>> >> > > in a branch, so those changes can be treated as a
rip / replace
>> >>> >> > > easily.
>> >>> >> > >
>> >>> >> > >>>
>> >>> >> > >>> >
>> >>> >> > >>> > 2. Media Types
>> >>> >> > >>> >
>> >>> >> > >>> > Initially support only application/json.
We can revisit
>> >>> >> > >>> > application/xml as a nice-to-have.
>> >>> >> > >>> >
>> >>> >> > >>>
>> >>> >> > >>> +1
>> >>> >> > >>>
>> >>> >> > >>> >
>> >>> >> > >>> > 3. HTTP Methods
>> >>> >> > >>> >
>> >>> >> > >>> > GET, PUT, POST, DELETE
>> >>> >> > >>> >
>> >>> >> > >>>
>> >>> >> > >>> +1 (We also need to decide if PUT can handle
partial updates)
>> >>> >> > >>>
>> >>> >> > >>
>> >>> >> > >> I say not.  That is what PATCH is for, once everything
>> supports it:
>> >>> >> > >> http://tools.ietf.org/html/rfc5789
>> >>> >> > >
>> >>> >> > > My understanding is that PUT should always be a full
object
>> >>> replace. A
>> >>> >> > > quick search returns the suggestion to use PATCH,
or to use
>> POST to
>> >>> a
>> >>> >> > > subresource with a 303 response.
>> >>> >> > >
>> >>> >> > >>
>> >>> >> > >>>
>> >>> >> > >>> >
>> >>> >> > >>> > 4. Status Codes
>> >>> >> > >>> >
>> >>> >> > >>> > 200, 201, 400, 401, 403, 404, 500
>> >>> >> > >>> >
>> >>> >> > >>>
>> >>> >> > >>> +1
>> >>> >> > >>>
>> >>> >> > >>> >
>> >>> >> > >>> > 5. URL formats
>> >>> >> > >>> >
>> >>> >> > >>> > Use plural nouns (pages, people, widgets).
Do not nest
>> >>> associations
>> >>> >> > >>> > beyond one level deep. For example:
>> >>> >> > >>> > /pages/1/regions (ok)
>> >>> >> > >>> > /pages/1/regions/2/regionwidgets (not
ok)
>> >>> >> > >>> > /regions/2/regionwidgets (ok)
>> >>> >> > >>> >
>> >>> >> > >>>
>> >>> >> > >>> I'm not a fan of this requirement. Your example
is the exact
>> >>> reason
>> >>> >> > I'm not
>> >>> >> > >>> a fan actually. In all reality, regions don't
mean anything
>> >>> outside a
>> >>> >> > page,
>> >>> >> > >>> and region widgets don't mean anything outside
of a region.
>> Yes,
>> >>> they
>> >>> >> > have
>> >>> >> > >>> IDs, but in reality, those IDs should be
subordinate to the
>> parent
>> >>> >> (so
>> >>> >> > >>> there should be nothing wrong with having
Page 1 with regions
>> >>> [1,2]
>> >>> >> and
>> >>> >> > >>> Page 2 with regions [1,2]). I understand
that's not how the DB
>> >>> works
>> >>> >> > today
>> >>> >> > >>> but it's what makes the most logical sense.
>> >>> >> > >>>
>> >>> >> > >>
>> >>> >> > >> I agree with Chris. We should not limit to a
single level.
>> That is
>> >>> >> > counter
>> >>> >> > >> to a few REST web service principles.
>> >>> >> > >>
>> >>> >> > >
>> >>> >> > > Fair enough. In this case I guess I would just be
looking for
>> >>> >> > > consistency - will associations be infinitely nest-able.
If not,
>> >>> what
>> >>> >> > > is the rule to determine where we support more or
less deeply
>> nested
>> >>> >> > > associations.
>> >>> >> > >
>> >>> >> > >>
>> >>> >> > >>> >
>> >>> >> > >>> > 6. Response formats
>> >>> >> > >>> >
>> >>> >> > >>> > 6a. Wrap all responses in an object.
All valid (200)
>> responses
>> >>> >> should
>> >>> >> > >>> > be wrapped in an object that includes
a "meta" object for
>> >>> metadata,
>> >>> >> > >>> > and a "data" object for the response
body. This allows us to
>> >>> >> capture
>> >>> >> > >>> > or extend metadata associated with a
response as needed. Any
>> >>> >> metadata
>> >>> >> > >>> > properties should be standardized.
>> >>> >> > >>> >
>> >>> >> > >>> > Example:
>> >>> >> > >>> >
>> >>> >> > >>> > GET /people
>> >>> >> > >>> > {
>> >>> >> > >>> >  meta: {count: 253, limit: 10, offset:
0, ...}
>> >>> >> > >>> >  data: [ {id: 1, name: 'canonical',
...}, ... ]
>> >>> >> > >>> > }
>> >>> >> > >>> >
>> >>> >> > >>> > GET /people/1
>> >>> >> > >>> > {
>> >>> >> > >>> >  meta: { ... }
>> >>> >> > >>> >  data: {id:1, name: 'canonical', ...}
>> >>> >> > >>> > }
>> >>> >> > >>> >
>> >>> >> > >>>
>> >>> >> > >>> This really complicates a couple things,
first, it means the
>> GET
>> >>> !=
>> >>> >> PUT
>> >>> >> > >>> since the GET will include the meta data.
Can we achieve this
>> same
>> >>> >> > result
>> >>> >> > >>> with HTTP Headers?
>> >>> >> > >>>
>> >>> >> > >
>> >>> >> > > We could possibly achieve the same with HTTP headers.
I prefer
>> the
>> >>> >> > > object approach for clarity, since custom http headers
are less
>> >>> >> > > accessible or discoverable than object structure.
I get your
>> point,
>> >>> >> > > but I see the wrapped object approach used commonly
in major
>> apis.
>> >>> If
>> >>> >> > > it's clearly documented and used consistently across
the entire
>> api
>> >>> I
>> >>> >> > > don't really see an issue.
>> >>> >> > >
>> >>> >> > >>> >
>> >>> >> > >>> > 6b. Error objects. In the case of an
error, the correct
>> error
>> >>> code
>> >>> >> > >>> > should be returned. In addition, an
error object should be
>> >>> returned
>> >>> >> > >>> > with a standardized format. Ideally
including a verbose,
>> >>> >> > >>> > human-readable error message for developers,
and an
>> >>> >> internationalized
>> >>> >> > >>> > readable error message for display to
end users.
>> >>> >> > >>> >
>> >>> >> > >>> > GET /people/25
>> >>> >> > >>> > 401
>> >>> >> > >>> > {
>> >>> >> > >>> >  developerMessage: 'Unauthorized. Access
to this resource
>> >>> requires
>> >>> >> > >>> > authentication',
>> >>> >> > >>> >  userMessage: 'Please login',
>> >>> >> > >>> >  stackTrace: ...
>> >>> >> > >>> > }
>> >>> >> > >>> >
>> >>> >> > >>>
>> >>> >> > >>> +1
>> >>> >> > >>>
>> >>> >> > >>> >
>> >>> >> > >>> > 6c. Partial responses. By default all
responses, whether a
>> list
>> >>> or
>> >>> >> > >>> > individual resource, should return a
full representation of
>> the
>> >>> >> > >>> > resources (not including security constraints).
 All
>> endpoints
>> >>> >> should
>> >>> >> > >>> > support the query string parameter "fields",
which accepts a
>> >>> comma
>> >>> >> > >>> > delimited list of fields to build a
partial response.
>> >>> >> > >>> >
>> >>> >> > >>>
>> >>> >> > >>> Hmmm.....what's funny (except for the wasted
work) is this is
>> how
>> >>> I
>> >>> >> > >>> originally  built the people resource. I
changed it because
>> the
>> >>> >> > "fields"
>> >>> >> > >>> approach gets almost impossible to manage
with nested
>> elements (at
>> >>> >> > least in
>> >>> >> > >>> Java - rewrite in Ruby anyone??). I'm open
to suggestions
>> though.
>> >>> I
>> >>> >> > guess
>> >>> >> > >>> we could also make a rule that the data objects
shouldn't have
>> >>> nested
>> >>> >> > >>> elements but that is a tough rule.
>> >>> >> > >>>
>> >>> >> > >>
>> >>> >> > >> I think the fields approach makes sense long-term;
but, it is
>> not
>> >>> >> > critical.
>> >>> >> > >>
>> >>> >> > >>
>> >>> >> > >
>> >>> >> > > I don't really know what the implementation looks
like. If you
>> allow
>> >>> >> > > field filtering only on properties and deliver only
properties
>> (i.e.
>> >>> >> > > no nested objects / associations) then I would assume
it is
>> pretty
>> >>> >> > > straightforward.
>> >>> >> > >>>
>> >>> >> > >>> >
>> >>> >> > >>> > GET /people/1
>> >>> >> > >>> > {
>> >>> >> > >>> >  meta: { ... },
>> >>> >> > >>> >  data: { id: 1, name: 'canonical', email:
'
>> canonical@gmail.com
>> >>> ',
>> >>> >> > ... }
>> >>> >> > >>> > }
>> >>> >> > >>> >
>> >>> >> > >>> > GET /people/1?fields=id,name
>> >>> >> > >>> > {
>> >>> >> > >>> >  meta: { ... },
>> >>> >> > >>> >  data: { id: 1, name: 'canonical' }
>> >>> >> > >>> > }
>> >>> >> > >>> >
>> >>> >> > >>> > 6d. Pagination. All requests that return
a list should be
>> >>> >> paginated.
>> >>> >> > >>> > The query string parameters "limit"
and "offset" should be
>> used
>> >>> for
>> >>> >> > >>> > pagination. On any request in which
either parameter is not
>> set,
>> >>> >> they
>> >>> >> > >>> > should default to 10 and 0 respectively.
>> >>> >> > >>> >
>> >>> >> > >>>
>> >>> >> > >>> +1
>> >>> >> > >>>
>> >>> >> > >>> >
>> >>> >> > >>> > 6e. Use camelCase for properties.
>> >>> >> > >>> >
>> >>> >> > >>>
>> >>> >> > >>> +1
>> >>> >> > >>>
>> >>> >> > >>> >
>> >>> >> > >>> > 7. Endpoints.
>> >>> >> > >>> >
>> >>> >> > >>> > 7a. Standard endpoints: there should
be standard CRUD
>> endpoints
>> >>> to
>> >>> >> > >>> > support each rave resource. In other
words, any operation
>> >>> possible
>> >>> >> in
>> >>> >> > >>> > rave should be possible through a rest
api action.
>> >>> >> > >>> >
>> >>> >> > >>>
>> >>> >> > >>> +1
>> >>> >> > >>>
>> >>> >> > >>> >
>> >>> >> > >>> > 7b. Special endpoints. In the case of
certain client needs,
>> we
>> >>> can
>> >>> >> > >>> > implement a small number of special
endpoints to fulfill a
>> >>> specific
>> >>> >> > >>> > role. The primary case in point is retrieving
a page for
>> render,
>> >>> >> > which
>> >>> >> > >>> > returns a page, its regions, its regionWidgets,
and their
>> render
>> >>> >> > data.
>> >>> >> > >>> >
>> >>> >> > >>>
>> >>> >> > >>> +1
>> >>> >> > >>>
>> >>> >> > >>> >
>> >>> >> > >>> >
>> >>> >> > >>> >
>> >>> >> > >>> > Ok, I think that's it. This is meant
as a proposal only -
>> we are
>> >>> >> > >>> > looking for feedback to go forward.
Thoughts?
>> >>> >> > >>> >
>> >>> >> > >>>
>> >>> >> >
>> >>> >>
>> >>>
>>

Mime
View raw message