source: Dev/trunk/src/client/qed-client/store/JsonRest.js @ 513

Last change on this file since 513 was 487, checked in by hendrikvanantwerpen, 11 years ago

Completed migration to API, without CouchDB proxy.

Move to API is now completed. The full API is password protected, a very
limited API is exposed for respondents, which works with secrets that
are passed in URLs.

Serverside the HTTPResult class was introduced, which is similar to
Promises, but specifically for HTTP. It carries a status code and
response and makes it easier to extract parts of async handling in
separate functions.

Fixed a bug in our schema (it seems optional attributes don't exist but
a required list does). Verification of our schema by grunt-tv4 didn't
work yet. Our schema is organized the wrong way (this is fixable),
but the json-schema schema has problems with simple types and $refs.

File size: 8.7 KB
Line 
1define(["../xhr", "dojo/_base/lang", "dojo/json", "dojo/io-query", "dojo/_base/declare", "dojo/store/util/QueryResults" /*=====, "./api/Store" =====*/
2], function(xhr, lang, JSON, ioQuery, declare, queryResults /*=====, Store =====*/){
3
4    // No base class, but for purposes of documentation, the base class is dojo/store/api/Store
5    var base = null;
6    /*===== base = Store; =====*/
7
8    /*=====
9    var __HeaderOptions = {
10            // headers: Object?
11            //        Additional headers to send along with the request.
12        },
13        __PutDirectives = declare(Store.PutDirectives, __HeaderOptions),
14        __QueryOptions = declare(Store.QueryOptions, __HeaderOptions);
15    =====*/
16
17    return declare("dojo.store.JsonRest", base, {
18        // summary:
19        //        This is a basic store for RESTful communicating with a server through JSON
20        //        formatted data. It implements dojo/store/api/Store.
21
22        constructor: function(options){
23            // summary:
24            //        This is a basic store for RESTful communicating with a server through JSON
25            //        formatted data.
26            // options: dojo/store/JsonRest
27            //        This provides any configuration information that will be mixed into the store
28            this.headers = {};
29            declare.safeMixin(this, options);
30        },
31
32        // headers: Object
33        //        Additional headers to pass in all requests to the server. These can be overridden
34        //        by passing additional headers to calls to the store.
35        headers: {},
36
37        // target: String
38        //        The target base URL to use for all requests to the server. This string will be
39        //        prepended to the id to generate the URL (relative or absolute) for requests
40        //        sent to the server
41        target: "",
42
43        // idProperty: String
44        //        Indicates the property to use as the identity property. The values of this
45        //        property should be unique.
46        idProperty: "id",
47
48        // sortParam: String
49        //        The query parameter to used for holding sort information. If this is omitted, than
50        //        the sort information is included in a functional query token to avoid colliding
51        //        with the set of name/value pairs.
52
53        // ascendingPrefix: String
54        //        The prefix to apply to sort attribute names that are ascending
55        ascendingPrefix: "+",
56
57        // descendingPrefix: String
58        //        The prefix to apply to sort attribute names that are ascending
59        descendingPrefix: "-",
60
61
62        get: function(id, options){
63            // summary:
64            //        Retrieves an object by its identity. This will trigger a GET request to the server using
65            //        the url `this.target + id`.
66            // id: Number
67            //        The identity to use to lookup the object
68            // options: Object?
69            //        HTTP headers. For consistency with other methods, if a `headers` key exists on this object, it will be
70            //        used to provide HTTP headers instead.
71            // returns: Object
72            //        The object in the store that matches the given id.
73            options = options || {};
74            var headers = lang.mixin({ Accept: this.accepts }, this.headers, options.headers || options);
75            return xhr("GET", {
76                url: this.target + id,
77                handleAs: "json",
78                headers: headers
79            });
80        },
81
82        // accepts: String
83        //        Defines the Accept header to use on HTTP requests
84        accepts: "application/javascript, application/json",
85
86        getIdentity: function(object){
87            // summary:
88            //        Returns an object's identity
89            // object: Object
90            //        The object to get the identity from
91            // returns: Number
92            return object[this.idProperty];
93        },
94
95        put: function(object, options){
96            // summary:
97            //        Stores an object. This will trigger a PUT request to the server
98            //        if the object has an id, otherwise it will trigger a POST request.
99            // object: Object
100            //        The object to store.
101            // options: __PutDirectives?
102            //        Additional metadata for storing the data.  Includes an "id"
103            //        property if a specific id is to be used.
104            // returns: dojo/_base/Deferred
105            options = options || {};
106            var id = ("id" in options) ? options.id : this.getIdentity(object);
107            var hasId = typeof id !== "undefined";
108            return xhr(hasId && !options.incremental ? "PUT" : "POST", {
109                url: hasId ? this.target + id : this.target,
110                postData: JSON.stringify(object),
111                handleAs: "json",
112                headers: lang.mixin({
113                    "Content-Type": "application/json",
114                    Accept: this.accepts,
115                    "If-Match": options.overwrite === true ? "*" : null,
116                    "If-None-Match": options.overwrite === false ? "*" : null
117                }, this.headers, options.headers)
118            });
119        },
120
121        add: function(object, options){
122            // summary:
123            //        Adds an object. This will trigger a PUT request to the server
124            //        if the object has an id, otherwise it will trigger a POST request.
125            // object: Object
126            //        The object to store.
127            // options: __PutDirectives?
128            //        Additional metadata for storing the data.  Includes an "id"
129            //        property if a specific id is to be used.
130            options = options || {};
131            options.overwrite = false;
132            return this.put(object, options);
133        },
134
135        remove: function(id, options){
136            // summary:
137            //        Deletes an object by its identity. This will trigger a DELETE request to the server.
138            // id: Number
139            //        The identity to use to delete the object
140            // options: __HeaderOptions?
141            //        HTTP headers.
142            options = options || {};
143            return xhr("DELETE", {
144                url: this.target + id,
145                headers: lang.mixin({}, this.headers, options.headers)
146            });
147        },
148
149        query: function(query, options){
150            // summary:
151            //        Queries the store for objects. This will trigger a GET request to the server, with the
152            //        query added as a query string.
153            // query: Object
154            //        The query to use for retrieving objects from the store.
155            // options: __QueryOptions?
156            //        The optional arguments to apply to the resultset.
157            // returns: dojo/store/api/Store.QueryResults
158            //        The results of the query, extended with iterative methods.
159            options = options || {};
160
161            var headers = lang.mixin({ Accept: this.accepts }, this.headers, options.headers);
162
163            if(options.start >= 0 || options.count >= 0){
164                //set X-Range for Opera since it blocks "Range" header
165                headers.Range = headers["X-Range"] = "items=" + (options.start || '0') + '-' +
166                    (("count" in options && options.count !== Infinity) ?
167                        (options.count + (options.start || 0) - 1) : '');
168            }
169            var hasQuestionMark = this.target.indexOf("?") > -1;
170            if(query && typeof query === "object"){
171                query = ioQuery.objectToQuery(query);
172                query = query ? (hasQuestionMark ? "&" : "?") + query: "";
173            }
174            if(options && options.sort){
175                var sortParam = this.sortParam;
176                query += (query || hasQuestionMark ? "&" : "?") + (sortParam ? sortParam + '=' : "sort(");
177                for(var i = 0; i<options.sort.length; i++){
178                    var sort = options.sort[i];
179                    query += (i > 0 ? "," : "") + (sort.descending ? this.descendingPrefix : this.ascendingPrefix) + encodeURIComponent(sort.attribute);
180                }
181                if(!sortParam){
182                    query += ")";
183                }
184            }
185            var results = xhr("GET", {
186                url: this.target + (query || ""),
187                handleAs: "json",
188                headers: headers
189            });
190            results.total = results.then(function(){
191                var range = results.ioArgs.xhr.getResponseHeader("Content-Range");
192                return range && (range = range.match(/\/(.*)/)) && +range[1];
193            });
194            return queryResults(results);
195        }
196    });
197
198});
Note: See TracBrowser for help on using the repository browser.