Skip to content

Commit

Permalink
Merge pull request #89 from kyse/master
Browse files Browse the repository at this point in the history
Reworked the promise chain and added error correction handling
  • Loading branch information
devnixs authored Oct 4, 2016
2 parents 49d3336 + 6b190b4 commit e64d11d
Show file tree
Hide file tree
Showing 6 changed files with 1,316 additions and 442 deletions.
190 changes: 147 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,42 @@
[![Build Status](https://travis-ci.org/devnixs/ODataAngularResources.svg?branch=master)](https://travis-ci.org/devnixs/ODataAngularResources)
[![Coverage Status](https://coveralls.io/repos/devnixs/ODataAngularResources/badge.svg?branch=master&15)](https://coveralls.io/r/devnixs/ODataAngularResources?branch=master)

# ODataAngularResources
# Table of Contents
* [How to install](#how-to-install)
* [How to use](#how-to-use)
* [Simple query](#simple-query)
* [Retrieving a single element](#retrieving-a-single-element)
* [Query with top, orderBy and skip](#query-with-top-orderby-and-skip)
* [Count and InlineCount](#count-and-inlinecount)
* [Including related models (Expanding)](#expanding)
* [Pick a subset of the properties (Selecting)](#selecting)
* [Specifying a custom url and method](#custom-url)
* [Specifying the response format](#format)
* [Advanced queries](#advanced-queries)
* [Predicates](#predicates)
* [Overriding default Predicate or Filter behavior](#override-default-predicate-or-filter-behavior)
* [Specifying the type of the data](#specifying-data-types)
* [Function calls](#function-calls)
* [Function calls definition](#function-calls-definition)
* [List of available functions](#function-calls-available-list)
* [Lambda Operators](#lambda-operators)
* [OData V4 support](#odata-v4)
* [Updating entries with OData v4](#odata-v4-action-methods)
* [Expand and Odata v4](#odata-v4-expand)
* [InlineCount with OData v4](#odata-v4-inlinecount)
* [Transform the final query url](#odata-v4-transform-url)
* [The Promise Chain](#promise-chain)
* [Interceptors](#promise-chain-interceptors)
* [Callbacks](#promise-chain-callbacks)
* [Error Correction](#promise-chain-error-correction)
* [Refreshing Responses & Odata Query Persistence](#saving-query-settings)
* [Persistence](#persistence)
* [$refresh](#refresh-data)
* [Build from the source](#build-from-source)
* [Run the tests](#unit-testing)
* [Contribute](#contribute)

# <a name="odataangularresource"></a>ODataAngularResources

ODataAngularResources is a fork of Angular's $resource that allows to make OData queries in a fluent way.
It does everything Angular Resources does but add some features:
Expand All @@ -13,7 +48,7 @@ It does everything Angular Resources does but add some features:

[Simple JSFiddle Demo](http://jsfiddle.net/h22f7596/)

##How to install
## <a name="how-to-install"></a>How to install

1. Download the repository or install the **bower package** :
```Shell
Expand All @@ -29,9 +64,9 @@ var myModule = angular.module('myModule',['ODataResources']);
myModule.controller('MyController', ['$scope','$odataresource',function($scope,$odataresource){}]);
```

##How to use
## <a name="how-to-use"></a>How to use

###Simple query
### <a name="simple-query"></a>Simple query
* Call the odata() method on your resource.
```javascript
var User = $odataresource('/user/:userId', {userId:'@id'});
Expand All @@ -57,7 +92,7 @@ var myUsers = User.odata()
});
```

### Retrieving a single element
### <a name="retrieving-a-single-element"></a>Retrieving a single element
* Simply call the get method with the entity key
```javascript
var userId = 10;
Expand Down Expand Up @@ -85,7 +120,7 @@ var myUser = User.odata()
```


###Query with top, orderBy and skip
### <a name="query-with-top-orderby-and-skip"></a>Query with top, orderBy and skip
```javascript
var User = $odataresource('/user/:userId', {userId:'@id'});
var myUsers = User.odata()
Expand All @@ -101,7 +136,7 @@ var myUsers = User.odata()
- Multiple chained filters are executed with **and** between.
- orderBy assumes the order to be asc if the second parameter is not specified.

### Count and InlineCount
### <a name="count-and-inlinecount"></a>Count and InlineCount
- It's possible to query the number of elements
```javascript
var data = User.odata().filter('name','bob').count();
Expand Down Expand Up @@ -131,7 +166,7 @@ var myUsers = User.odata()
// users.count == 10
```

###Including related models (expanding)
### <a name="expanding"></a>Including related models (Expanding)
* You can easily include related models by calling the expand method
```javascript
var User = $odataresource('/user/:userId', {userId:'@id'});
Expand Down Expand Up @@ -161,21 +196,21 @@ var myUsers = User.odata()

//Queries /user?$expand=City,Orders
```
###Pick a subset of the properties (Selecting)
### <a name="selecting"></a>Pick a subset of the properties (Selecting)
* You can use the select method to retrieve only some properties of the entities.
```javascript
var User = $odataresource('/user/:userId', {
userId: '@id'
});
var users = User.odata().select(['name','userId']).query();
//OR
var users = User.odata().select('name','userId').query();
var User = $odataresource('/user/:userId', {
userId: '@id'
});

var users = User.odata().select(['name','userId']).query();
//OR
var users = User.odata().select('name','userId').query();

//Queries /user?$select=userId,name
```

###Specifying a custom url and method
### <a name="custom-url"></a>Specifying a custom url and method
* Want a custom url for your odata queries? easy! It works just like angular resources:
```javascript
User = $odataresource('/user/:userId',
Expand All @@ -189,7 +224,7 @@ User = $odataresource('/user/:userId',
);
```

###Specifying the response format
### <a name="format"></a>Specifying the response format
```javascript
var myUsers = User.odata()
.format("json")
Expand All @@ -201,9 +236,9 @@ var myUsers = User.odata()
```


##Advanced queries
## <a name="advanced-queries"></a>Advanced queries

###Predicates
### <a name="predicates"></a>Predicates
* If you need to write or statements in your queries, you need to use the Predicate class.
First, be sure to reference the **$odata** dependency.
```javascript
Expand Down Expand Up @@ -242,7 +277,7 @@ User.odata().filter(predicate).query();
//Queries /user?$filter=((FirstName eq 'John') or (LastName ne 'Doe')) and Age gt 10
```

### Overriding default Predicate or Filter behavior
### <a name="override-default-predicate-or-filter-behavior"></a>Overriding default Predicate or Filter behavior
It is sometime necessary to compare two properties or two values in a query.
To do so, you can use the $odata.Value or $odata.Property classes
```javascript
Expand All @@ -263,7 +298,7 @@ User.odata().filter(
//Queries /user?$filter=Name eq Surname
```

### Specifying the type of the data
### <a name="specifying-data-types"></a>Specifying the type of the data

This library is clever enough to figure out the types from the data passed and format them accordingly.
But sometimes you may need to have a specific output type. In this case you can pass a second argument to the $odata.Value() constructor :
Expand Down Expand Up @@ -311,17 +346,17 @@ Int32 | 51358
String | 'Hello OData'


###Function calls
### <a name="function-calls"></a>Function calls
* You can call functions like endswith or length on an OData query.
To do so, use the **$odata.Func** class.
```javascript
var users = User.odata()
.filter(new $odata.Func("endswith","FullName","Doe"), true)
.query();
.filter(new $odata.Func("endswith","FullName","Doe"), true)
.query();
//Queries /user?$filter=endswith(FullName, 'Doe') eq true
```

####Definition
#### <a name="function-calls-definition"></a>Definition

new $odata.Func(**MethodName**, **PropertyName**, **Value1**, **Value2**,...)

Expand All @@ -330,12 +365,11 @@ The parameters are assumed to be first, a property and then a value.
This behavior can be overriden by specifying explicit values or properties :
```javascript
new $odata.Func('substringof',
new $odata.Value('Alfreds'),
new $odata.Property('CompanyName')
);
new $odata.Value('Alfreds'),
new $odata.Property('CompanyName'));
```

####List of available functions
#### <a name="function-calls-available-list"></a>List of available functions

Function | Example | Example value
--------- | --------- | -----------
Expand Down Expand Up @@ -369,7 +403,7 @@ decimal ceiling(decimal p0) | new $odata.Func('floor','Freight') | 33
**Type Functions** | |
bool IsOf(expression p0, type p1) | new $odata.Func('isof','ShipCountry', 'Edm.String') | true

### Lambda Operators
### <a name="lambda-operators"></a>Lambda Operators
The **$odata.Func** class also supports the lambda operators **any** and **all**

new $odata.Func(**LambdaOperator**, **PropertyName**, **LambdaVariable**, **Expression**)
Expand All @@ -390,7 +424,7 @@ Jobs.odata().filter(func).query();
```


### OData V4 support
## <a name="odata-v4"></a>OData V4 support
This project supports basic odata v4 queries and responses.
If the server responds with an array wrapped inside an object :
```json
Expand Down Expand Up @@ -421,7 +455,7 @@ console.log(myUsers.totalCount);

```

#### Updating entries with OData v4
### <a name="odata-v4-action-methods"></a>Updating entries with OData v4
You can use the $update method on an object.
But for that you need to specify what is the property that contains the key.

Expand Down Expand Up @@ -459,7 +493,7 @@ myUser.$update();
//will issue a POST /user(id=1,roleid=2)
```

#### Expand and Odata v4
### <a name="odata-v4-expand"></a>Expand and Odata v4

With odatav4 expanding nested entities is done with a different query
```
Expand Down Expand Up @@ -507,7 +541,7 @@ var query = User.odata().expandPredicate("roles").select("name").finish().expand
// /user?$expand=roles($select=name),provider($select=name;expand=settings,providertype)
```

### InlineCount with OData v4
### <a name="odata-v4-inlinecount"></a>InlineCount with OData v4

- With OData v4 inlinecount issues a $count=true parameter
```javascript
Expand All @@ -530,7 +564,7 @@ var query = User.odata().expandPredicate("roles").select("name").finish().expand
// And then, the count will be defined as followed
// users.count == 10
```
### Transform the final query url
### <a name="odata-v4-transform-url"></a>Transform the final query url

- It is possible to transform the query that will be made to the server by calling the method transformUrl
```javascript
Expand All @@ -545,15 +579,85 @@ User.odata()
// queries /user?$filter=Name eq 'Raphael'&foo=bar
```

### Refreshing Responses & Odata Query Persistence
## <a name="promise-chain"></a>The Promise Chain
When you retrieve a resource from the API, you are returned a Resource object or Array of Resources with the $http call being appended to the
$promise property. This promise has already been chained through two phases. A intereptor phase for $http request success or errors. These
are not to be confused with $http request and response interceptors, but follow the same ceonepts with argumets and return values. After the
interceptor phase is the callback phase for per request success or error handling.

### <a name="promise-chain-interceptors"></a>Interceptors
Interceptor handlers can be defined for each action you define for the actions object argument to $odataresource. Both succes and error
interceptor handlers get called withe the $http response object passed as their single argument. There is a default handler defined for the
success interceptor phase on each action defined with $odataresource that returns argument.resource. To maintain continuous support from
$odataresource prototype methods (save, update, delete, etc), you should return this object from you success interceptor if you override the
default one.
```javascript
User = $odataresource('/user', {}, {
odata: {
interceptor: {
response: function(response) {
// response is the returned $http result with the parsed Resource object on response.resource.
apiLog.logSuccess(response);
return response.resource;
},
responseError: function(response) {
// response is the returned $http result
apiLog.logError(response, response.headers());
},
},
},
}, {});
```

### <a name="promise-chain-callbacks"></a>Callbacks
Callbacks can be provided to each API call by passing a method reference in the parameters to the $odataresource api call. The success callback
is provided if you want a convient way to inject into the promise chain when calling the api query. Both the final Resource object and the $http
response headers are provided as parameters an available success callback handler. The error callback handler will provide you with any error
message that might get thrown during parsing the $http response and building the Resource object.
```javascript
var User = $odataresource("/user", {}, {}, {});
var user = User.odata().select("FirstName","LastName").query(function(resource, headers) {
resource.Name = resource.FirstName + ' ' + resource.LasstName;
}, function(error) {
apiLog.logError(error);
});
```

### <a name="promise-chain-error-correction"></a>Error Correction
The error interceptor and callback handling has been enhanced to provide an opportunity for error handling and correction measures to be applied
to the original $odataresource settings. The response from both the error interceptor and the error callback can provide corrected data back to
the promise chain to merge into the final Resource object. The correction can be in the form of a new Resource object, a new Resource object
promise, an $http promise, or an object with a property named $correction containing new arguments for the $oataresource object (url, defaultParams,
actions, options). If the new promise, or correction results in another error, the error correction attempt will stop and reject, preventing
an infinate loop.
```javascript
User = $odataresource('/user', {}, {
odata: {
interceptor: {
response: function(response) {
// response is the returned $http result with the parsed Resource object on response.resource.
apiLog.logSuccess(response);
return response.resource;
},
responseError: function(response) {
// response is the returned $http result
apiLog.logError(response, response.headers());
return { $correction: { url: 'https://fixedUrl.com/' } };
},
},
},
}, {});
```

## <a name="saving-query-settings"></a>Refreshing Responses & Odata Query Persistence
Support has been added to keep track of queries used to retrieve entities. You can call the $refresh method on a returned array of resources,
or an individual resource object itself to get an updated entity from the API. The odata query applied to the refresh GET will depend on how
the object you're calling the $refresh method on was retrieved. There are two types of persisted queries, full and limited. Full will store
the entire list of odata arguments you supplied to reproduce the same result set. Limited will limit the query to selects, expands, and
format. Limited assumes you're getting a single entity from a larger query, and want to keep the response equal to the initial object, ie
same selects, expands, but dont need the filter/take/skip/etc odata arguments.

**Persistence**
### <a name="persistence"></a>Persistence

To apply persisted query arguments to the provider manually call the 're' method.
```javascript
Expand All @@ -567,7 +671,7 @@ var user = User.odata().select("name").query();
var newUser = user.$odata().query();
```

**$refresh**
### <a name="refresh-data"></a>$refresh

Persistence is applied automatically with or without the persistence options flag when using $refresh.

Expand All @@ -583,7 +687,7 @@ var count = resource.odata().count(); | var count = count.$refresh(); | fu



### Build from the source
## <a name="build-from-source"></a>Build from the source

1. You need Grunt installed globally:
```sh
Expand All @@ -595,13 +699,13 @@ var count = resource.odata().count(); | var count = count.$refresh(); | fu
> grunt build
```

### Run the tests
## <a name="unit-testing"></a>Run the tests
* Simply run
```sh
> grunt test
```

### Contribute
## <a name="contribute"></a>Contribute

Want to contribute? Great!
Be sure to write tests before submitting your pull request.
Loading

0 comments on commit e64d11d

Please sign in to comment.