Giter Club home page Giter Club logo

agrest's People

Contributors

aarrsseni avatar akazimirski avatar andrus avatar atomashpolskiy avatar const1993 avatar irus avatar kravchenkoas avatar m-dzianishchyts avatar rzen avatar scali avatar stariy95 avatar thinline72 avatar vvertinskiy avatar vyarmolovich avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

agrest's Issues

Idempotent create or update with ByKeyAndParentObjectMapper results in noop updates

Minor... A code like this servicing PUT:

    service.idempotentCreateOrUpdate(E2.class)
            .toManyParent(E1.class, id, _E1.E2S)
            .mapper(ByKeyAndParentObjectMapper.byKeyAndParent(_E2.NAME))
            .with(uriInfo)
            .readConstraints(cosntraints).process(data);

I run the same PUT request 3 times. Each request contains 2 E2s. First request generates 2 INSERTs (correct), second and third requests run 2 UPDATEs, updating E2's parent PK - to the same parent ID. This still results in correct data, but UPDATE is not needed and only hits the DB for no good reason.

Expected: no UPDATEs.

CreateOrUpdateBuilder

This is a massive refactoring of the insert/update flow that gives us consistent bulk requests with optional idempotent behavior.

Since we have so many intersecting options (insert,update, insert-or-update , idempotent or not, with implied parent relationship or without one), we need a way for the users to build a backend request they need in each specific situation. I came up with an interface like that:

ILinkRestService:

<T> CreateOrUpdateBuilder<T> update(Class<T> type);
<T> CreateOrUpdateBuilder<T> create(Class<T> type);
<T> CreateOrUpdateBuilder<T> createOrUpdate(Class<T> type);

/**
 * Returns a CreateOrUpdateBuilder that would perform an idempotent
 * create-or-update operation on the request objects. The operation will
 * fail if it can't be executed as idempotent. The condition is usually that
 * all object's ID should be passed explicitly in request or can be implied
 * from a relationship. Otherwise the server will have no way of mapping
 * update data to an existing object and the update can't be idempotent.
 */
<T> CreateOrUpdateBuilder<T> idempotentCreateOrUpdate(Class<T> type);

CreateOrUpdateBuilder:

/**
 * A builder for create (insert) or update operations for a single entity type.
 */
public interface CreateOrUpdateBuilder<T> {

/**
 * Set an explicit id for the update. In this case only a single object is
 * allowed in the update.
 */
CreateOrUpdateBuilder<T> id(Object id);

/**
 * Sets up a relationship clause for all objects in this update.
 */
CreateOrUpdateBuilder<T> parent(Class<?> parentType, Object parentId, String relationshipFromParent);

/**
 * Sets up a relationship clause for all objects in this update.
 */
CreateOrUpdateBuilder<T> parent(Class<?> parentType, Object parentId, Property<T> relationshipFromParent);

/**
 * Sets up a relationship clause for all objects in this update.
 */
CreateOrUpdateBuilder<T> toManyParent(Class<?> parentType, Object parentId,
        Property<? extends Collection<T>> relationshipFromParent);

UpdateResponse<T> process(String entityData);
}

This will effectively deprecate current "insert" and "update" operations (the are a special case of the builder) and remove all the recent related/relatedNew API (which is hopefully not widely used yet).

Chatty merging of config

For entities that exclude most attributes on the server, a default request with no includes or excludes results in a bunch of complaints in the logs:

IntersectConfigMerger: Attribute not allowed, removing: bla
IntersectConfigMerger: Attribute not allowed, removing: foo

Would be nice to only complain about explicit includes that were denied. This also hints that merging after creating a default request filled with all entity attributes may be a suboptimal approach.

DataResponseConfig should be attached to SelectBuilder

Currently DataResponseConfig is created by calling ILinkRestService and the instance is then passed to SelectBuilder. This only makes possible to configure it once and outside of SelectBuilder. Per #14 LinkRest needs to create its own DataResponseConfig for relationship requests. I guess it makes sense to keep a single instance of DataResponseConfig inside SelectBuilder created lazily. Then it can be initially configured for a given request by LinkRest, and users can customize it as needed.

So....

ILinkRestService.newConfig // will be removed
SelectBuilder.withConfig // will be removed
SelectBuilder<T> withEntity(EntityConfigBuilder builder); // will be added
SelectBuilder<T> fetchOffset(int offset); // will be added
SelectBuilder<T> fetchLimit(int limit); // will be added

Prebuilt entity configurations can still be stored and reused as EntityConfigBuilder instances.

SenchaAdapter with Incoming id filter

This feature is needed to properly handle Sencha requests. For new objects Sencha generates phantom IDs (e.g. "id" : "MyEntity-123", but this customizable - http://docs.sencha.com/extjs/5.0/apidocs/#!/api/Ext.data.identifier.Generator ) . And LinkRest 1.3 can mistake those for real DB ids. So ideally we need an update pipeline filter that would match the ID patterns and replace them with nulls.

We'll use this opportunity to start work on SenchaAdapter , implementing LinkRestAdapter (see #24 ) to provide Sencha-specific bindings.

Refactoring: rename NoRolesEntityAuthorizationEncoderFilter to EntityEncoderFilter

Originally we used NoRolesEntityAuthorizationEncoderFilter as a superclass for authorization filters. Though the code in it is not related to authorization at all. In fact the only thing it does is applying the filter to a specific entity. So will need to rename it to EntityEncoderFilter and rename the abstract methods accordingly.

LinkRestAdapter - a generic LR extension mechanism

Define LinkRestAdapter interface - a generic LR extension mechanism that would allow to customize a bunch of services at once. This would allow Sencha specific extensions, but also pretty much any kind of customization that LR DI allows. Here is a proposed API, allowing to contribute to both internal Injector and JAX RS Feature:

public interface LinkRestAdapter {
    void contributeToRuntime(Binder binder);
    void contributeToJaxRs(Collection<Feature> features);
}

Batch lookup of relationship objects.

Currently various LinkRest operations that are using ObjectMapper are done per-object. If we are updating/deleting large collections , this is going to result in visible performance impact. Need to batch all relationship ops.

Support for implicit propagated ids on create/update

Updating many-to-many relationship N ... NM ... M. Join table NM has its own meaningful columns so it can't be flattened. NM has a compound PK made of FKs to N and M. An update JSON may look like this:

  { "m":1,"n":2}
  { "m":1} # if "n" is propagated from parent.

I.e. there's no explicit ID, but instead there are propagated IDs which can come from parent of the request and/or from relationship keys.

Customizable request chain

We need to make request chain steps customizable ... there should be an ability to include/exclude request keys defined in the protocol at https://github.com/nhl/link-rest/wiki/LinkRest-Protocol per stack (and maybe later per request). For now this will be achieved by making the following services injectable:

IFilterProcessor
ISortProcessor
ITreeProcessor

Will also split "group", "groupDir" and "filter" keys to SenchaAdapter.

Server-side includes

We are supporting includes on the client, that can be arbitrarily nested. Would be cool to give server a way to restrict that ability per request, as there are clearly security and performance implications. So here is what's suggested:

  • Support server-side includes, same as we do for client.
  • If no server-side includes are explicitly set , use "root entity attributes" as a default.
  • Merging of client and server includes will be done based on configurable policy:
    • "Intersection" - default - allows server to define maximum allowed span of the includes and the client to request a subset.
    • "Union" - client and server includes are combined together. Less secure. Though may save some repetitive client code.

Support for char PK

We need to handle all schemas supported by Cayenne:

java.lang.IllegalArgumentException: PK is not a number: ObjectId:....
at com.nhl.link.rest.encoder.NumericObjectIdEncoder.encodeNonNullObject(NumericObjectIdEncoder.java:38)
at com.nhl.link.rest.encoder.AbstractEncoder.encode(AbstractEncoder.java:19)
at com.nhl.link.rest.property.PropertyBuilder.encode(PropertyBuilder.java:60)
at com.nhl.link.rest.encoder.EntityToOneEncoder.encode(EntityToOneEncoder.java:26)
at com.nhl.link.rest.property.PropertyBuilder.encode(PropertyBuilder.java:60)
at com.nhl.link.rest.encoder.EntityEncoder.encodeNonNullObject(EntityEncoder.java:29)
at com.nhl.link.rest.encoder.AbstractEncoder.encode(AbstractEncoder.java:19)
at com.nhl.link.rest.encoder.RootListEncoder.encode(RootListEncoder.java:97)
at com.nhl.link.rest.encoder.RootListEncoder.encode(RootListEncoder.java:59)
at com.nhl.link.rest.DataResponse.writeData(DataResponse.java:153)
at com.nhl.link.rest.provider.DataResponseWriter.writeData(DataResponseWriter.java:23)
at com.nhl.link.rest.provider.DataResponseWriter.writeData(DataResponseWriter.java:13)
at com.nhl.link.rest.provider.BaseResponseWriter$1.generateJSON(BaseResponseWriter.java:50)

Add "Location:" header to the update responses

Some of the examples are shown under #40 ... "Location:" header to point to the updated resource is a REST-ful way to show the client where to get the latest data, and makes returning full representation in the body optional.

Redesign default constraints as EntityConstraint's

TreeConstraints is a proper thing to constrain individual requests. Not so much when declaring per-entity constraints. The later form a graph, not a tree and using Tree structure causes issues like #36 . So this is a complete redesign of default constraints using different classes.

Add 'SelectBuilder.selectOne' method

When selecting by id, our code explicitly checks the size of the result, and returns 404 on 0 objects, and 500 on > 1 objects. But sometimes we'd like to select a single object based on a set of criteria that are not id (e.g. matching on a non-id unique column, etc.). To handle this case we will add SelectBuilder.selectOne method that will behave similar to the current selection by Id, no matter the query.

StackOverflow on compiling circular constraint annotations

When recursively compiling constraints from annotations, we can get into the endless loop, resulting in a stack overflow. We must account for the fact that Cayenne model is a graph , not a tree and keep track of processed entities as we are doing the compilation.

javax.ws.rs.ProcessingException: Server-side request processing failed with an error.
at org.glassfish.jersey.test.inmemory.InMemoryConnector$InMemoryResponseWriter.failure(InMemoryConnector.java:168)
at org.glassfish.jersey.server.ServerRuntime$Responder.process(ServerRuntime.java:433)
at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:265)
at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:320)
at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:236)
at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1028)
at org.glassfish.jersey.test.inmemory.InMemoryConnector.apply(InMemoryConnector.java:278)
at org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:225)
at org.glassfish.jersey.client.JerseyInvocation$1.call(JerseyInvocation.java:655)
at org.glassfish.jersey.client.JerseyInvocation$1.call(JerseyInvocation.java:652)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:424)
at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:652)
at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:387)
at org.glassfish.jersey.client.JerseyInvocation$Builder.get(JerseyInvocation.java:291)
at com.nhl.link.rest.incontainer.GET_Constraints_Test.test_Annotated(GET_Constraints_Test.java:64)

...
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

Caused by: java.lang.StackOverflowError
at org.apache.cayenne.di.Key.(Key.java:49)
at org.apache.cayenne.di.Key.get(Key.java:37)
at org.apache.cayenne.di.spi.DefaultInjector.getProvider(DefaultInjector.java:141)
at org.apache.cayenne.di.spi.DefaultInjector.getInstance(DefaultInjector.java:131)
at org.apache.cayenne.configuration.CayenneRuntime.getChannel(CayenneRuntime.java:165)
at com.nhl.link.rest.runtime.cayenne.CayennePersister.entityResolver(CayennePersister.java:34)
at com.nhl.link.rest.runtime.constraints.ConstraintsHandler.compile(ConstraintsHandler.java:112)
at com.nhl.link.rest.runtime.constraints.ConstraintsHandler.compileRead(ConstraintsHandler.java:102)
at com.nhl.link.rest.runtime.constraints.ConstraintsHandler.compile(ConstraintsHandler.java:132)
at com.nhl.link.rest.runtime.constraints.ConstraintsHandler.compileRead(ConstraintsHandler.java:102)
at com.nhl.link.rest.runtime.constraints.ConstraintsHandler.compile(ConstraintsHandler.java:132)
at com.nhl.link.rest.runtime.constraints.ConstraintsHandler.compileRead(ConstraintsHandler.java:102)
at com.nhl.link.rest.runtime.constraints.ConstraintsHandler.compile(ConstraintsHandler.java:132)
at com.nhl.link.rest.runtime.constraints.ConstraintsHandler.compileRead(ConstraintsHandler.java:102)
at com.nhl.link.rest.runtime.constraints.ConstraintsHandler.compile(ConstraintsHandler.java:132)

...

at com.nhl.link.rest.runtime.constraints.ConstraintsHandler.compileRead(ConstraintsHandler.java:102)
at com.nhl.link.rest.runtime.constraints.ConstraintsHandler.compile(ConstraintsHandler.java:132)
at com.nhl.link.rest.runtime.constraints.ConstraintsHandler.compileRead(ConstraintsHandler.java:102)
at com.nhl.link.rest.runtime.constraints.ConstraintsHandler.compile(ConstraintsHandler.java:132)
at com.nhl.link.rest.runtime.constraints.ConstraintsHandler.compileRead(ConstraintsHandler.java:102)
at com.nhl.link.rest.runtime.constraints.ConstraintsHandler.compile(ConstraintsHandler.java:132)
at com.nhl.link.rest.runtime.constraints.ConstraintsHandler.compileRead(ConstraintsHandler.java:102)
at com.nhl.link.rest.runtime.constraints.ConstraintsHandler.compile(ConstraintsHandler.java:132)
at com.nhl.link.rest.runtime.constraints.ConstraintsHandler.compileRead(ConstraintsHandler.java:102)
at com.nhl.link.rest.runtime.constraints.ConstraintsHandler.compile(ConstraintsHandler.java:132)
at com.nhl.link.rest.runtime.constraints.ConstraintsHandler.compileRead(ConstraintsHandler.java:102)
at com.nhl.link.rest.runtime.constraints.ConstraintsHandler.compile(ConstraintsHandler.java:132)
at com.nhl.link.rest.runtime.constraints.ConstraintsHandler.compileRead(ConstraintsHandler.java:102)
at com.nhl.link.rest.runtime.constraints.ConstraintsHandler.compile(ConstraintsHandler.java:132)
at com.nhl.link.rest.runtime.constraints.ConstraintsHandler.compileRead(ConstraintsHandler.java:102)
at com.nhl.link.rest.runtime.constraints.ConstraintsHandler.compile(ConstraintsHandler.java:132)
at com.nhl.link.rest.runtime.constraints.ConstraintsHandler.compileRead(ConstraintsHandler.java:102)

IMetadataService must throw LinkRestException on bad entities

IMetadataService currently returns null for bad entities. Most its users would check for null and throw an exception.. Types of exceptions vary. Sometimes they don't check for null, and this will result in an NPE downstream. So make sure IMetadataService consistently throws LinkRestException/BAD_REQUEST.

Note that this is not a huge deal as till now LinkRest API operated based on Java classes for all root entities, ignoring the possibility of specifying an entity specified dynamically. Although with relationships support (#14) it becomes more important to validate metadata names.

'filter' processor improvements

'filter' is an ExtJS style key that supports basic criteria filtering. We need to improve it to support full set of Ext options:

  1. It must support boolean key "exactMatch". If specified, value must be matched as is instead of using LIKE
  2. It must recognize numeric values, not just String and boolean
  3. It must support matching on relationship ids

'idempotentFullSync' method to synchronize collections

recent "createOrUpdate" functionality in LinkRestService doesn't go far enough. It doesn't allow to build a true PUT request on a collection (e.g. "update the state of 1..N"). It is half way there, but deleting items is not supported. Need to add another Java operation (preliminary name: "idempotentFullSync") that would treat incoming data as a full collection representation and hence would delete server items not in the collection.

There's synergy with #29 - once we switch to multi-object locators, it will be easy to implement delete.

rename CreateOrUpdateBuilder to just UpdateBuilder

With #38 CreateOrUpdateBuilder will be used to delete data , so createOrUpdate is no longer a proper name. So rename to UpdateBuilder. While this not an ideal name, it is more generic than CreateOrUpdateBuilder and actually matches UpdateResponse.

IEncoderService to return Encoder instead of DataResponse

The current IEncoderService API is too fancy for its own good:

 <T> DataResponse<T> makeEncoder(DataResponse<T> response)

This won't handle UpdateResponse pass-through; this does an implicit assignment of encoder to response. So let's simplify it and make more reusable (though a little more verbose) :

Encoder makeEncoder(DataResponse<?> response);

ILinkRestService API for managing relationship operations

This will support for relationships to LinkRest. Below we'll describe typical scenarios that will be handled, rather than the Java API that handles them. Based on the new API one may handle two types of resources - relationship ("collection") resources and relationship target resources.

Collection Resources

Here the resource is the entire relationship collection defined by the source entity, source object id and relationship name. Even if relationship is to-one, it is treated as a LinkRest data collection.

// Return a list of related entities for a give source
GET /foo/{sourceid}/{relationship}

// create new target and relate to source...  
// target data passed in the body
POST /foo/{sourceid}/{relationship}

// Break a relationship between source and all its target objects.
// If target objects can't exist without the source, each target object is deleted
DELETE /foo/{sourceid}/{relationship}

Target Resources

Here the resource is a relationship between two existing objects - source and target.

// create a relationship between existing source and target
// unless it already exists.
// additionally target data is updated from the body entity 

// this is intended as an idempotent operation, hence - PUT.
// but of course for now LinkRest is purely a Java framework
// so mapping its backend to REST can be done in any possible way
PUT /foo/{sourceid}/{relationship}/{targetid}

// Break a relationship between source and specific target.
// Target may also be deleted if it can't exist without a source
DELETE /foo/{sourceid}/{relationship}/{targetid}

Implementing Id encoder / decoder for 'compound' ID entities

We need a pluggable mechanism for semi-opaque ID encoding/decoding similar to cayenne-lifecycle IdCoder [1]. It should be compatible with the current implicit ID encoding. I.e. single column IDs should render as is. Multi-column should be concatenated with separator. Unlike IdCoder we don't need to include entity name in the encoded id. Also we probably need to implement some kind of escaping scheme to tell separators from ID bodies.

[1] https://github.com/apache/cayenne/blob/master/cayenne-lifecycle/src/main/java/org/apache/cayenne/lifecycle/id/IdCoder.java

Intercept Cayenne ValidationException

Right now a Cayenne ValidationException results in 500 response and the exception being exposed to the client. E.g.:

HTTP/1.1 500 Internal Server Error
Content-Length: 210
Content-Type: application/json
Vary: Accept-Encoding
Server: Jetty(8.1.4.v20120524)

{"success":false,
"message":"CayenneRuntimeException [v.3.2.M2.27fc609 May 15 2014 11:40:10] 
Validation failures: Validation failure for foo.Entity.property: 
 \"property\"  is required."}

What we can do here is create a ExceptionMapper and a MessageBodyWriter. This will bridge Cayenne validation layer with REST.

Optionally suppress entity bodies on update requests

Currently updates are done Sencha-style - the response of POST or PUT (but not DELETE) is always a full representation - our normal LinkRest collection. A more "purist" REST response would look like something like this for a single object:

HTTP/1.1 201 Created
Location: http://example.org/linkrest/myentity/123

HTTP/1.1 200 Ok
Location: http://example.org/linkrest/myentity/123

And this for collection:

HTTP/1.1 200 Ok
Location: http://example.org/linkrest/myentity/

I.e. the body is optional and normally omitted, and "Location" header is added that points to a modified resource. This should probably be the default, and returning full representation - a feature of the SenchaAdapter.

While we are not ready to supply the location header just yet (LinkRest has no notion of the resource URI mappings) , we can at least suppress the collection and let the caller to figure out the Location URI.

Refactor delete operation to DeleteBuilder with "delete by id" and "delete by parent" options.

The motivation for this is the following problem that happened when trying to "unrelate" all objects in a relationship:

Found this issue in 1.3 - 1..N:

  service.unrelate(E.class, id, _E.REL);

Results in Exception:

CommonsJdbcEventLogger: *** error.
java.sql.BatchUpdateException: Column 'E_ID' cannot be null
    at com.mysql.jdbc.PreparedStatement.executeBatchSerially(PreparedStatement.java:2054)
           ....
    at org.apache.cayenne.access.DataContext.commitChanges(DataContext.java:676)
    at com.nhl.link.rest.runtime.cayenne.CayenneDao.unrelate(CayenneDao.java:166)
    at com.nhl.link.rest.runtime.EntityDaoLinkRestService.unrelate(EntityDaoLinkRestService.java:119)

Side effect of this feature will be that "unrelate" will stop deleting objects

support for Filter operator option

Per ExtJS 5.0 Documentation:

operator : String

The operator to use to compare the property to this Filter's value

Possible values are:

  <
  <=
  =
  >=
  >
  !=
  in
  like

The in operator expects this filter's value to be an array and matches values that are present in that array.

The like operator matches values that contain this filter's value as a substring.

EntityConfig API improvements

  • Allow to clone DataResponseConfig, and its underlying EntityConfig. This would allow to reuse entity templates and tweak configs for special operations.

  • Add missing builder API to EntityConfig:

    paths(Property<?>...)
    paths(String...)
    and(Expression e) // rename from 'andQualifier'
    or(Expression e)

DataResponseConfig - a server-side request template

This is a more generic version of #2 . Essentially every protocol request parameter can be defined on the server , either globally or per request with the goal of either simplify the client's life, or more likely, restrict the client to a certain set of options.

This config object might mirror the structure of DataResponse, and merged into DataResponse according to the merge policy ("intersection", "union").

ID that is not a PK can't be used in idempotent requests

Ran into a situation of 1..N relationship with unique target collection, where target uniqueness for a given source is determined by some column. I.e.:

create table target (
   id PK,
   source_id FK,
   type 
);

So (source_id, type) are unique. Client does not know target id, but knows source ID and type. Link-rest gets confused when doing the following idempotent operation:

service.idempotentCreateOrUpdate(Target.class)
          .toManyParent(Source.class, id, _Source.TARGETS).process(data);

PUT:
{"type":"t1"}

{"success":false,"message":"Request is not idempotent. At least one update has no id"}

Same request with POST instead of PUT (createOrUpdate) results in new object created every time. I guess one way to solve this is to allow passing custom ObjectLocator (currently a private interface in CayenneCreateOrUpdateBuilder) to the builder.

Allow TreeConstraints to be applied to insert/update requests

Allow TreeConstraints to be applied to insert/update requests ... 2 separate configurations will define permissions to read and modify attributes and relationships. E.g.:

CreateOrUpdateBuilder

  • CreateOrUpdateBuilder readConstraints(TreeConstraints constraints);
  • CreateOrUpdateBuilder writeConstraints(TreeConstraints constraints);

Annotations for default constraints

Even with fluent API mapping constraints is an ugly task. Wouldn't it be cool if we can map default constraints with annotations on entity classes.

forSelectRelated/constraints causes unqualified fetch ; Refactor 'forSelectRelated' to be setup inside SelectBuilder

ILinkRestService has these methods:

- forSelectRelated(Class<?> root, Object sourceId, Property<T> relationship);
- forSelectRelated(Class<?> root, Object sourceId, String relationship);

As our experience in 1.3 shows, specifying relationships should better be done inside the builder. So deprecate these methods and add a method to SelectBuilder similar to CreateOrUpdateBuilder:

- parent(Class<?> parentType, Object parentId, Property<T> relationshipFromParent);
- parent(Class<?> parentType, Object parentId, String relationshipFromParent);

Add type generic parameter to TreeConstraint

See if we can genericize TreeConstraint . It is rooted in an entity after all, and it should be rather helpful to ensure we are applying the write one to a given request chain

Support "group" as an object

Currently LR supports "group" as an object array: group:[{"property":"blabla","direction":"ASC"}, ...].
It'll be nice to have "group" as an object supported. Example: group:{"property":"blabla","direction":"ASC"}

problem handling grouper:{} configuration

ExtJS 5 serializes grouper into an object (as opposed to an array with a single object). LR SortProcessor needs to be able to accept a single sorter represented by a single object.

Sencha: update objects order is important on bulk updates

With the latest refactoring done around 1.7 to support full sync operations, we've lost a guaranteed order of items on response from POST/PUT. This seems important to Ext/Sencha bulk update code. So will need to ensure the ordering is preserved. If it is not possible to achieve within a general code, this can be made a feature of Sencha adapter.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.