├── .gitignore
├── README.md
├── lib
├── google
│ ├── appengine
│ │ ├── api
│ │ │ ├── blobstore.js
│ │ │ ├── capabilities.js
│ │ │ ├── channel.js
│ │ │ ├── datastore.js
│ │ │ ├── datastore
│ │ │ │ ├── errors.js
│ │ │ │ └── types.js
│ │ │ ├── images.js
│ │ │ ├── mail.js
│ │ │ ├── memcache.js
│ │ │ ├── namespace-manager.js
│ │ │ ├── oauth.js
│ │ │ ├── quota.js
│ │ │ ├── taskqueue.js
│ │ │ ├── urlfetch.js
│ │ │ ├── users.js
│ │ │ ├── utils.js
│ │ │ └── xmpp.js
│ │ ├── ext
│ │ │ ├── blobstore.js
│ │ │ ├── blobstore
│ │ │ │ ├── downloadapp.js
│ │ │ │ └── uploadapp.js
│ │ │ ├── db.js
│ │ │ ├── db
│ │ │ │ ├── errors.js
│ │ │ │ ├── forms.js
│ │ │ │ ├── model.js
│ │ │ │ ├── properties.js
│ │ │ │ ├── query.js
│ │ │ │ ├── stats.js
│ │ │ │ └── utils.js
│ │ │ ├── deferred.js
│ │ │ ├── jsgi
│ │ │ │ └── namespace.js
│ │ │ ├── remote-api.js
│ │ │ ├── webapp.js
│ │ │ └── webapp
│ │ │ │ ├── blobstore-handlers.js
│ │ │ │ ├── mail-handlers.js
│ │ │ │ ├── template.js
│ │ │ │ ├── util.js
│ │ │ │ └── xmpp-handlers.js
│ │ ├── logging.js
│ │ ├── runtime.js
│ │ ├── tools
│ │ │ └── development
│ │ │ │ ├── sdk.js
│ │ │ │ └── testing.js
│ │ └── utils.js
│ └── apphosting
│ │ └── api
│ │ └── apiproxy.js
└── mapreduce
│ ├── operation.js
│ └── operation
│ ├── counters.js
│ └── db.js
├── package.json
└── test
├── google
└── appengine
│ └── ext
│ └── db
│ └── model.js
└── run.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.swp
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | App Engine JavaScript SDK
2 | =========================
3 |
4 | Welcome to App Engine for JavaScript! With App Engine, you can build web applications using the JavaScript programming language, and take advantage of the many libraries, tools and frameworks for JavaScript that professional developers use to build world-class web applications. Your JavaScript application runs on Google's scalable infrastructure on top of Java and Rhino, and uses large-scale persistent storage and services.
5 |
6 | *This is a community project, not affiliated in any way with Google.*
7 |
8 | * Source & Download: [http://github.com/gmosx/appengine/](http://github.com/gmosx/appengine/)
9 | * Status updates: [http://twitter.com/appenginejs](http://twitter.com/appenginejs)
10 | * Homepage: [http://appenginejs.org/](http://appenginejs.org/)
11 | * Documentation: [http://appenginejs.org/docs](http://appenginejs.org/docs)
12 | * Mailing list: [http://groups.google.com/group/appenginejs](http://groups.google.com/group/appenginejs)
13 | * Issue tracking: [http://github.com/gmosx/appengine/issues](http://github.com/gmosx/appengine/issues)
14 | * IRC: #appenginejs on [irc.freenode.net](http://freenode.net/)
15 |
16 | This SDK is part of the [Nitro](http://www.nitrojs.org) ecosystem of Web Application development resources. The SDK tracks the latest developments in the CommonJS group.
17 |
18 | This SDK is compatible with [RingoJS](http://www.ringojs.org). An older version worked with [Narwhal](http://www.narwhaljs.org) and it should be relatively easy to use the SDK with narwhal (some changes will be required though).
19 |
20 |
21 | Examples
22 | --------
23 |
24 | Two examples are provided for the library:
25 |
26 | * [appengine-example](http://www.nitrojs.org/appenginejs/appengine-example.tar.gz) - a very simple JSGI application that utilizes the datastore API
27 | * [appengine-blog-example](http://www.nitrojs.org/appenginejs/appengine-blog-example.tar.gz) - a simple blog powered by AppengineJS and Nitro.
28 |
29 | The above examples include all the packages required to run them to make setup easier.
30 |
31 |
32 | Documentation
33 | -------------
34 |
35 | For short examples on using the various API's please consult the [online documentation](http://www.appenginejs.org/docs).
36 |
37 |
38 | Credits
39 | -------
40 |
41 | * George Moschovitis [george.moschovitis@gmail.com](mailto:george.moschovitis@gmail.com)
42 | * Kyungwook, Park [parksama@gmail.com](mailto:parksama@gmail.com)
43 | * Panagiotis Astithas [pastith@gmail.com](mailto:pastith@gmail.com)
44 | * Christoph Dorn [christoph@christophdorn.com](mailto:christoph@christophdorn.com)
45 | * Roberto Saccon [rsaccon@gmail.com](mailto:rsaccon@gmail.com)
46 |
47 |
48 | Google App Engine
49 | -----------------
50 |
51 | This is a community project, not affiliated in any way with Google.
52 |
53 | [Google App Engine](http://appengine.google.com) is a service of Google, Inc. Copyright (c) 2009 [Google](http://www.google.com), all rights reserved.
54 |
55 |
56 | License
57 | -------
58 |
59 | Copyright (c) 2009-2010 George Moschovitis, [http://www.gmosx.com](http://www.gmosx.com)
60 |
61 | Permission is hereby granted, free of charge, to any person obtaining a copy
62 | of this software and associated documentation files (the "Software"), to
63 | deal in the Software without restriction, including without limitation the
64 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
65 | sell copies of the Software, and to permit persons to whom the Software is
66 | furnished to do so, subject to the following conditions:
67 |
68 | The above copyright notice and this permission notice shall be included in
69 | all copies or substantial portions of the Software.
70 |
71 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
72 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
73 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
74 | THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
75 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
76 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
77 |
--------------------------------------------------------------------------------
/lib/google/appengine/api/blobstore.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview A JavaScript blobstore API used by app developers.
3 | *
4 | * Contains methods uses to interface with Blobstore API. Defines db.Key-like
5 | * class representing a blob-key. Contains API part that forward to apiproxy.
6 | *
7 | * http://code.google.com/appengine/docs/java/blobstore/
8 | * http://code.google.com/appengine/docs/python/blobstore/
9 | */
10 |
11 | var JBlobstoreServiceFactory = Packages.com.google.appengine.api.blobstore.BlobstoreServiceFactory,
12 | jservice = JBlobstoreServiceFactory.getBlobstoreService(),
13 | JBlobKey = Packages.com.google.appengine.api.blobstore.BlobKey,
14 | JArrayList = java.util.ArrayList;
15 |
16 | var DATASTORE_TYPES = require("google/appengine/api/datastore/types"),
17 | BlobKey = DATASTORE_TYPES.BlobKey;
18 |
19 | exports.BLOB_INFO_KIND = "__BlobInfo__";
20 | exports.BLOB_KEY_HEADER = "X-AppEngine-BlobKey";
21 | exports.UPLOAD_INFO_CREATION_HEADER = "X-AppEngine-Upload-Creation";
22 |
23 | exports.createUploadUrl = function (successPath) {
24 | return String(jservice.createUploadUrl(successPath));
25 | };
26 |
27 | /**
28 | * Delete a blob from Blobstore.
29 | *
30 | * Args:
31 | * blobKeys: Single instance or list of blob keys. A blob-key can be either
32 | * a string or an instance of BlobKey.
33 | */
34 | exports.remove = exports.DELETE = function (blobKeys) {
35 | if (isArray(blobKeys)) {
36 | if (keys.length > 0) {
37 | var list = new JArrayList(blobKeys.length);
38 | for (var i = 0; i < blobKeys.length; i++) list.add(new JBlobKey(blobKeys[i].toString()));
39 | jservice["delete"](list);
40 | }
41 | } else {
42 | jservice["delete"](new JBlobKey(blobKeys.toString()));
43 | }
44 | };
45 |
46 |
47 | //------------------------------------------------------------------------------
48 | // Extension of the Python API, converted from the Java API.
49 |
50 | /**
51 | * Returns the BlobKey for any files that were uploaded.
52 | */
53 | exports.getUploadedBlobs = function (request) {
54 | var map = jservice.getUploadedBlobs(request.env.servletRequest),
55 | blobKeys = {};
56 |
57 | for (var i in Iterator(map.entrySet())){
58 | var blobKey = new BlobKey(i.getValue());
59 | blobKeys[String(i.getKey())] = new BlobKey(String(i.getValue().getKeyString()));
60 | }
61 |
62 | return blobKeys;
63 | };
64 |
65 | /**
66 | * Arrange for the specified blob to be served as the response content for the
67 | * current request.
68 | * WARNING: The API may change in a future version.
69 | *
70 | * @param {BlobKey} blobKey Blob-key to serve in response.
71 | * @param {Request} request JSGI request.
72 | */
73 | exports.serve = function (blobKey, request) {
74 | var servletResponse = request.env.servletResponse;
75 | jservice.serve(new JBlobKey(blobKey.toString()), request.env.servletResponse);
76 |
77 | // Skip the JSGI response, just return the Servlet response.
78 | return {
79 | headers: {
80 | "X-JSGI-Skip-Response": "true"
81 | }
82 | };
83 | };
84 |
--------------------------------------------------------------------------------
/lib/google/appengine/api/capabilities.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Allows applications to identify API outages and scheduled downtime.
3 | *
4 | * NOT IMPLEMENTED
5 | */
6 |
--------------------------------------------------------------------------------
/lib/google/appengine/api/channel.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Channel API.
3 | *
4 | * The Channel API creates a persistent connection between your application and Google servers, allowing
5 | * your application to send messages to JavaScript clients in real time without the use of polling.
6 | *
7 | * Usage :
8 | *
9 | * Step 1. Create channel
10 | *
11 | * var channel = require("google/appengine/api/channel"),
12 | * tokenForFoo = null,
13 | * tokenForBar = null;
14 | *
15 | * tokenForFoo = channel.createChannel("Foo"); // "Foo" should use this token.
16 | * tokenForBar = channel.createChannel("Bar"); // "Bar" should use this token.
17 | *
18 | * Step 2. Send message
19 | *
20 | * var channel = require("google/appengine/api/channel");
21 | *
22 | * channel.sendMessage("Bar", "Hello"); // Send message "Hello" to "Bar"
23 | * channel.sendMessage("Foo", "World"); // Send message "World" to "Foo"
24 | *
25 | */
26 | var JChannelServiceFactory = Packages.com.google.appengine.api.channel.ChannelServiceFactory,
27 | JChannelMessage = Packages.com.google.appengine.api.channel.ChannelMessage,
28 | JChannelService = JChannelServiceFactory.getChannelService();
29 |
30 | /**
31 | * Creates a channel.
32 | *
33 | * @param {String} client_id - An application-created string the server uses to identify individual JavaScript clients.
34 | * @return {String} Returns a token for use by the JavaScript client listening on the channel.
35 | */
36 | exports.createChannel = function(client_id) {
37 | var token = null;
38 | try {
39 | token = JChannelService.createChannel(client_id);
40 | } catch (exception) {
41 | throw exception;
42 | }
43 | return token;
44 | };
45 |
46 | /**
47 | * Asynchronously sends a message to a channel.
48 | * No error is returned if the message cannot be delivered.
49 | *
50 | * @param {String} client_id - The key identifying a JavaScript client.
51 | * @param {String} message - The message passed to the client.
52 | */
53 | exports.sendMessage = function(client_id, message) {
54 | try {
55 | JChannelService.sendMessage(new JChannelMessage(client_id, message));
56 | } catch (exception) {
57 | throw exception;
58 | }
59 | };
--------------------------------------------------------------------------------
/lib/google/appengine/api/datastore.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Datastore API
3 | * http://code.google.com/appengine/docs/java/datastore/
4 | * http://code.google.com/appengine/docs/python/datastore/
5 | */
6 |
7 | var JDATASTORE = Packages.com.google.appengine.api.datastore,
8 | JDatastoreServiceFactory = JDATASTORE.DatastoreServiceFactory,
9 | JDatastore = exports.Datastore = JDatastoreServiceFactory.getDatastoreService();
10 |
11 | var JKey = exports.Key = JDATASTORE.Key;
12 | exports.KeyFactory = JDATASTORE.KeyFactory;
13 |
14 | var JEntity = exports.Entity = JDATASTORE.Entity;
15 | exports.Query = JDATASTORE.Query;
16 |
17 | var JDatastoreServiceConfigBuilder = JDATASTORE.DatastoreServiceConfig.Builder,
18 | JReadPolicy = JDATASTORE.ReadPolicy;
19 |
20 | /** @const */ exports.STRONG_CONSISTENCY = new JReadPolicy(JReadPolicy.Consistency.STRONG);
21 | /** @const */ exports.EVENTUAL_CONSISTENCY = new JReadPolicy(JReadPolicy.Consistency.EVENTUAL);
22 |
23 | /**
24 | * Normalizes and type checks the given argument.
25 | *
26 | * Args:
27 | * arg: an instance, tuple, list, iterator, or generator of the given type(s)
28 | * types: allowed type or tuple of types
29 | *
30 | * Returns:
31 | * A (list, bool) tuple. The list is a normalized, shallow copy of the
32 | * argument. The boolean is true if the argument was a sequence, false
33 | * if it was a single object.
34 | *
35 | * Raises:
36 | * AssertionError: types includes list or tuple.
37 | * BadArgumentError: arg is not an instance or sequence of one of the given
38 | * types.
39 | */
40 | exports.normalizeAndTypeCheck = function (arg, types) {
41 | if (isArray(arg)) {
42 | return [arg, true];
43 | } else {
44 | return [[arg], false];
45 | }
46 | }
47 |
48 | exports.normalizeAndTypeCheckKeys = function (arg, types) {
49 | var parts = normalizeAndTypeCheck(keys, ["string", Key, JEntity]),
50 | keys = parts[0],
51 | multiple = parts[1];
52 |
53 | return [keys.map(function (k) { return getCompleteKeyOrError(k) }), multiple];
54 | }
55 |
56 | var getCompleteKeyOrError = exports.getCompleteKeyOrError = function (arg) {
57 | var key;
58 |
59 | if (arg.constructor == Key) {
60 | key = arg;
61 | } else if (typeof(arg) == "string") {
62 | key = new Key(arg);
63 | } // check JEntity
64 | /*
65 | if (!key.hasKeyOrId()) {
66 | throw BadKeyError("Key " + key + " is not complete");
67 | }
68 | */
69 | return key;
70 | }
71 |
72 | /**
73 | * Specialized RPC for the datastore.
74 | *
75 | * Wraps the default RPC class and sets appropriate values for use by the
76 | * datastore.
77 | *
78 | * This class or a sublcass of it is intended to be instatiated by
79 | * developers interested in setting specific request parameters, such as
80 | * deadline, on API calls. It will be used to make the actual call.
81 | *
82 | * @constructor
83 | */
84 | var DatastoreRPC = exports.DatastoreRPC = function (args) {
85 | this.args = args;
86 | }
87 |
88 | DatastoreRPC.prototype.getDatastore = function () {
89 | if (this.args) {
90 | var config = JDatastoreServiceConfigBuilder.withReadPolicy(this.args.readPolicy || exports.STRONG_CONSISTENCY);
91 | if (this.args.deadline) {
92 | config.deadline(this.args.deadline);
93 | }
94 | return JDatastoreServiceFactory.getDatastoreService(config);
95 | } else {
96 | return JDatastoreServiceFactory.getDatastoreService();
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/lib/google/appengine/api/datastore/errors.js:
--------------------------------------------------------------------------------
1 | // FIXME: these errors are defined but not used yet.
2 |
3 | var defineError = require("google/appengine/utils").defineError;
4 |
5 | /**
6 | * Raised when a property value or filter value is invalid.
7 | */
8 | exports.BadValueError = defineError("BadValueError");
9 |
10 | /**
11 | * Raised when a property name isn't a string.
12 | */
13 | exports.BadPropertyError = defineError("BadPropertyError");
14 |
15 | /**
16 | * Raised by datastore calls when the parameter(s) are invalid.
17 | */
18 | exports.BadRequestError = defineError("BadRequestError");
19 |
20 | /**
21 | * Raised by Query.Order(), Iterator.Next(), and others when they're
22 | * passed an invalid argument.
23 | */
24 | exports.BadArgumentError = defineError("BadArgumentError");
25 |
26 | /**
27 | * May be raised by transaction functions when they want to roll back
28 | * instead of committing. Note that *any* exception raised by a transaction
29 | * function will cause a rollback. This is purely for convenience. See
30 | * datastore.RunInTransaction for details.
31 | */
32 | exports.Rollback = defineError("Rollback");
33 |
34 | /**
35 | * Raised by RunInTransaction methods when the transaction could not be
36 | * committed, even after retrying. This is usually due to high contention.
37 | */
38 | exports.TransactionFailedError = defineError("TransactionFailedError");
39 |
40 | /**
41 | * Raised by Query.run() when a filter string is invalid.
42 | */
43 | exports.BadFilterError = defineError("BadFilterError");
44 |
45 | /**
46 | * Raised by Query when a query or query string is invalid.
47 | */
48 | exports.BadQueryError = defineError("BadQueryError");
49 |
50 | /**
51 | * Raised by Key.toString() when the key is invalid.
52 | */
53 | exports.BadKeyError = defineError("BadKeyError");
54 |
55 | /**
56 | * An internal datastore error. Please report this to Google.
57 | */
58 | exports.InternalError = defineError("InternalError");
59 |
60 | /**
61 | * No matching index was found for a query that requires an index. Check
62 | * the Indexes page in the Admin Console and your index.yaml file.
63 | */
64 | exports.NoIndexError = defineError("NoIndexError");
65 |
66 | /**
67 | * The datastore operation timed out, or the data was temporarily
68 | * unavailable. This can happen when you attempt to put, get, or delete too
69 | * many entities or an entity with too many properties, or if the datastore is
70 | * overloaded or having trouble.
71 | */
72 | exports.Timeout = defineError("Timeout");
73 |
74 | /**
75 | * The write or transaction was committed, but some entities or index rows
76 | * may not have been fully updated. Those updates should automatically be
77 | * applied soon. You can roll them forward immediately by reading one of the
78 | * entities inside a transaction.
79 | */
80 | exports.CommitedButStillapplying = defineError("CommitedButStillapplying", Timeout);
81 |
82 |
--------------------------------------------------------------------------------
/lib/google/appengine/api/datastore/types.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Higher-level, semantic data types for the datastore. These types
3 | * are expected to be set as attributes of Entities. See "Supported Data Types"
4 | * in the API Guide.
5 | *
6 | * Most of these types are based on XML elements from Atom and GData elements
7 | * from the atom and gd namespaces. For more information, see:
8 | *
9 | * http://www.atomenabled.org/developers/syndication/
10 | * http://code.google.com/apis/gdata/common-elements.html
11 | *
12 | * The namespace schemas are:
13 | *
14 | * http://www.w3.org/2005/Atom
15 | * http://schemas.google.com/g/2005
16 | */
17 |
18 | var JDatastoreServiceFactory = Packages.com.google.appengine.api.datastore.DatastoreServiceFactory,
19 | JDatastore = JDatastoreServiceFactory.getDatastoreService(),
20 | JKey = Packages.com.google.appengine.api.datastore.Key,
21 | JKeyFactory = Packages.com.google.appengine.api.datastore.KeyFactory,
22 | JString = Packages.java.lang.String;
23 |
24 | /**
25 | * The primary key for a datastore entity.
26 | *
27 | * A datastore GUID. A Key instance uniquely identifies an entity across all
28 | * apps, and includes all information necessary to fetch the entity from the
29 | * datastore with Get().
30 | *
31 | * @constructor
32 | */
33 | var Key = exports.Key = function (encoded) {
34 | this.__key__ = JKeyFactory.stringToKey(encoded);
35 | }
36 |
37 | Key.create = function (options) {
38 | var dsKey;
39 |
40 | if (options.name) {
41 | if (options.parent) {
42 | dsKey = JKeyFactory.createKey(options.parent.datastoreKey(), options.kind, options.name);
43 | } else {
44 | dsKey = JKeyFactory.createKey(options.kind, options.name);
45 | }
46 | } else {
47 | if (options.parent) {
48 | dsKey = JDatastore.allocateIds(options.parent.datastoreKey(), options.kind, 1).getStart();
49 | } else {
50 | dsKey = JDatastore.allocateIds(options.kind, 1).getStart();
51 | }
52 | }
53 |
54 | return Key.fromDatastoreKey(dsKey);
55 | }
56 |
57 | Key.fromDatastoreKey = function (dsKey) {
58 | var key = Object.create(Key.prototype);
59 | key.__key__ = dsKey;
60 | return key;
61 | }
62 |
63 | /**
64 | * Construct a Key out of a "path" (kind, id or name, ...).
65 | *
66 | * This is useful when an application wants to use just the id or name portion
67 | * of a key in e.g. a URL, where the rest of the URL provides enough context to
68 | * fill in the rest, i.e. the app id (always implicit), the entity kind, and
69 | * possibly an ancestor key. Since ids and names are usually small, they're
70 | * more attractive for use in end-user-visible URLs than the full string
71 | * representation of a key.
72 | *
73 | * Args:
74 | * kind: the entity kind (a str or unicode instance)
75 | * id_or_name: the id (an int or long) or name (a str or unicode instance)
76 | *
77 | * Additional positional arguments are allowed and should be
78 | * alternating kind and id/name.
79 | *
80 | * Keyword args:
81 | * parent: optional parent Key; default None.
82 | *
83 | * Returns:
84 | * A new Key instance whose .kind() and .id() or .name() methods return
85 | * the *last* kind and id or name positional arguments passed.
86 | *
87 | * Raises:
88 | * BadArgumentError for invalid arguments.
89 | * BadKeyError if the parent key is incomplete.
90 | */
91 | Key.fromPath = function () {
92 | throw Error("Not implemented");
93 | }
94 |
95 | /**
96 | * Returns this entity's app id, a string.
97 | */
98 | Key.prototype.app = function () {
99 | throw Error("Not implemented");
100 | }
101 |
102 | /**
103 | * Returns this entity's namespace, a string.
104 | */
105 | Key.prototype.namespace = function () {
106 | throw Error("Not implemented");
107 | }
108 |
109 | Key.prototype.appIdNamespace = function () {
110 | throw Error("Not implemented");
111 | }
112 |
113 | Key.prototype.kind = function () {
114 | return this.__key__.getKind();
115 | }
116 |
117 | Key.prototype.id = function () {
118 | return this.__key__.getId();
119 | }
120 |
121 | Key.prototype.name = function () {
122 | return this.__key__.getName();
123 | }
124 |
125 | Key.prototype.parent = function () {
126 | return Key.fromDatastoreKey(this.__key__.getParent());
127 | }
128 |
129 | Key.prototype.key = function () {
130 | return this;
131 | }
132 |
133 | Key.prototype.datastoreKey = function () {
134 | return this.__key__;
135 | }
136 |
137 | Key.prototype.toString = function () {
138 | return JKeyFactory.keyToString(this.__key__);
139 | }
140 |
141 | Key.prototype.valueOf = function () {
142 | return JKeyFactory.keyToString(this.__key__);
143 | }
144 |
145 | Key.prototype.toJSON = Key.prototype.toString;
146 |
147 | /**
148 | * @constructor
149 | */
150 | var Category = exports.Category = function (tag) {
151 | }
152 |
153 | /**
154 | * Key used to identify a blob in Blobstore.
155 | *
156 | * This object wraps a string that gets used internally by the Blobstore API
157 | * to identify application blobs. The BlobKey corresponds to the entity name
158 | * of the underlying BlobReference entity.
159 | *
160 | * This class is exposed in the API in both google.appengine.ext.db and
161 | * google.appengine.ext.blobstore.
162 | *
163 | * Constructor:
164 | *
165 | * Used to convert a string to a BlobKey. Normally used internally by
166 | * Blobstore API.
167 | *
168 | * Args:
169 | * blobKey: Key name of BlobReference that this key belongs to.
170 | *
171 | * @constructor
172 | */
173 | var BlobKey = exports.BlobKey = function (blobKey) {
174 | // TODO: ValidateString(blob_key, 'blob-key')
175 | this.blobKey = blobKey;
176 | }
177 |
178 | BlobKey.prototype.toString = function () {
179 | return this.blobKey;
180 | }
181 |
182 |
183 | /**
184 | * A tag, ie a descriptive word or phrase. Entities may be tagged by users,
185 | * and later returned by a queries for that tag. Tags can also be used for
186 | * ranking results (frequency), photo captions, clustering, activity, etc.
187 | *
188 | * Here's a more in-depth description: http://www.zeldman.com/daily/0405d.shtml
189 | *
190 | * This is the Atom "category" element. In XML output, the tag is provided as
191 | * the term attribute. See:
192 | * http://www.atomenabled.org/developers/syndication/#category
193 | *
194 | * Raises BadValueError if tag is not a string or subtype.
195 | *
196 | * @constructor
197 | */
198 | var Category = exports.Category = function (tag) {
199 | }
200 |
201 |
202 | /**
203 | * A fully qualified URL. Usually http: scheme, but may also be file:, ftp:,
204 | * news:, among others.
205 | *
206 | * @constructor
207 | */
208 | var Link = exports.Link = function (link) {
209 | }
210 |
211 | /**
212 | * An RFC2822 email address. Makes no attempt at validation; apart from
213 | * checking MX records, email address validation is a rathole.
214 | *
215 | * This is the gd:email element. In XML output, the email address is provided as
216 | * the address attribute. See:
217 | * http://code.google.com/apis/gdata/common-elements.html#gdEmail
218 | *
219 | * @constructor
220 | */
221 | var Email = exports.Email = function () {
222 | }
223 |
224 | /**
225 | * A geographical point, specified by floating-point latitude and longitude
226 | * coordinates. Often used to integrate with mapping sites like Google Maps.
227 | * May also be used as ICBM coordinates.
228 | *
229 | * This is the georss:point element. In XML output, the coordinates are
230 | * provided as the lat and lon attributes. See: http://georss.org/
231 | *
232 | * Serializes to ','. Raises BadValueError if it's passed an invalid
233 | * serialized string, or if lat and lon are not valid floating points in the
234 | * ranges [-90, 90] and [-180, 180], respectively.
235 | *
236 | * @constructor
237 | */
238 | // THINK: use more javascriptish latitude, longitude?
239 | var GeoPt = exports.GeoPt = function (lat, lon) {
240 | this.lat = lat;
241 | this.lon = lon;
242 | }
243 |
244 | GeoPt.prototype.toXML = function () {
245 | return "" + this.lat + " " + this.lon + "";
246 | }
247 |
--------------------------------------------------------------------------------
/lib/google/appengine/api/images.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Image manipulation API.
3 | */
4 |
5 | var JImagesServiceFactory = Packages.com.google.appengine.api.images.ImagesServiceFactory,
6 | JImagesService = Packages.com.google.appengine.api.images.ImagesService,
7 | JImage = Packages.com.google.appengine.api.images.Image,
8 | jservice = JImagesServiceFactory.getImagesService();
9 |
10 | var ByteString = require("binary").ByteString;
11 |
12 | /*
13 | var bytestring = function(bytes) {
14 | var b = new ByteString();
15 | b._bytes = bytes;
16 | b._offset = 0;
17 | b._length = Number(b._bytes.length);
18 |
19 | return b;
20 | }
21 | */
22 |
23 | var PNG = /** @const */ exports.PNG = JImagesService.OutputEncoding.PNG;
24 | var JPEG = /** @const */ exports.JPEG = JImagesService.OutputEncoding.JPEG;
25 |
26 | /**
27 | * An instance of the Image class represents a single image to which multiple
28 | * transformations can be applied. Methods on the instance set up
29 | * transformations, which are executed all at once when the executeTransforms()
30 | * method is called.
31 | *
32 | * @constructor
33 | * @param {ByteString} imageData The raw image data.
34 | */
35 | var Image = exports.Image = function(imageData) {
36 | // this.image = JImagesServiceFactory.makeImage(imageData._bytes);
37 | this.image = JImagesServiceFactory.makeImage(imageData);
38 | this.transform = JImagesServiceFactory.makeCompositeTransform();
39 | this.width = Number(this.image.getWidth());
40 | this.height = Number(this.image.getHeight());
41 | }
42 |
43 | /**
44 | * Resizes an image, scaling down or up to the given width and height.
45 | */
46 | Image.prototype.resize = function(width, height) {
47 | this.transform.concatenate(JImagesServiceFactory.makeResize(width || 0, height || 0));
48 | return this;
49 | }
50 |
51 | /**
52 | * Crops an image to a given bounding box. The method returns the transformed
53 | * image in the same format. The left, top, right and bottom of the bounding box
54 | * are specified as proportional distances. The coordinates of the bounding box
55 | * are determined as left_x * width, top_y * height, right_x * width and
56 | * bottom_y * height. This allows you to specify the bounding box independently
57 | * of the final width and height of the image, which may change simultaneously
58 | * with a resize action.
59 | */
60 | Image.prototype.crop = function(leftX, topY, rightX, bottomY) {
61 | this.transform.concatenate(JImagesServiceFactory.makeCrop(leftX, topY, rightX, bottomY));
62 | return this;
63 | }
64 |
65 | /**
66 | * Rotates an image. The amount of rotation must be a multiple of 90 degrees.
67 | * Rotation is performed clockwise. A 90 degree turn rotates the image so that
68 | * the edge that was the top becomes the right edge.
69 | */
70 | Image.prototype.rotate = function(degrees) {
71 | this.transform.concatenate(JImagesServiceFactory.makeRotate(degrees));
72 | return this;
73 | }
74 |
75 | /**
76 | * Flips an image horizontally. The edge that was the left becomes the right
77 | * edge, and vice versa.
78 | */
79 | Image.prototype.horizontalFlip = function() {
80 | this.transform.concatenate(JImagesServiceFactory.makeHorizontalFlip());
81 | return this;
82 | }
83 |
84 | /**
85 | * Flips an image vertically. The edge that was the top becomes the bottom edge,
86 | * and vice versa.
87 | */
88 | Image.prototype.verticalFlip = function() {
89 | this.transform.concatenate(JImagesServiceFactory.makeVerticalFlip());
90 | return this;
91 | }
92 |
93 | /**
94 | * Executes all transforms set for the Image instance by the above methods, and
95 | * returns the result.
96 | */
97 | Image.prototype.executeTransforms = function(outputEncoding) {
98 | if (!outputEncoding) outputEncoding = PNG;
99 | var newImage = jservice.applyTransform(this.transform, this.image, outputEncoding);
100 |
101 | return bytestring(newImage.getImageData());
102 | }
103 |
104 | /**
105 | * Resizes an image, scaling down or up to the given width and height. The
106 | * function takes the image data to resize, and returns the transformed image in
107 | * the same format.
108 | *
109 | * imageData = ByteString of image data.
110 | *
111 | * Returns a ByteString containing the transformed image data.
112 | */
113 | exports.resize = function(imageData, width, height, outputEncoding) {
114 | var image = new Image(imageData)
115 | image.resize(width, height);
116 | return image.executeTransforms(outputEncoding);
117 | }
118 |
119 | /**
120 | * Crops an image to a given bounding box. The function takes the image data to
121 | * crop, and returns the transformed image in the same format.
122 | *
123 | * The left, top, right and bottom of the bounding box are specified as
124 | * proportional distances. The coordinates of the bounding box are determined as
125 | * left_x * width, top_y * height, right_x * width and bottom_y * height. This
126 | * allows you to specify the bounding box independently of the final width and
127 | * height of the image, which may change simultaneously with a resize action.
128 | */
129 | exports.crop = function(imageData, leftX, topY, rightX, bottomY, outputEncoding) {
130 | var image = new Image(imageData)
131 | image.crop(leftX, topY, rightX, bottomY);
132 | return image.executeTransforms(outputEncoding);
133 | }
134 |
135 | /**
136 | * Rotates an image. The amount of rotation must be a multiple of 90 degrees.
137 | * The function takes the image data to rotate, and returns the transformed
138 | * image in the same format.
139 | *
140 | * Rotation is performed clockwise. A 90 degree turn rotates the image so that
141 | * the edge that was the top becomes the right edge.
142 | */
143 | exports.rotate = function(imageData, degrees, outputEncoding) {
144 | var image = new Image(imageData)
145 | image.rotate(degrees);
146 | return image.executeTransforms(outputEncoding);
147 | }
148 |
149 | /**
150 | * Flips an image horizontally. The edge that was the left becomes the right
151 | * edge, and vice versa.
152 | */
153 | exports.horizontalFlip = function() {
154 | var image = new Image(imageData)
155 | image.horizontalFlip();
156 | return image.executeTransforms(outputEncoding);
157 | }
158 |
159 | /**
160 | * Flips an image vertically. The edge that was the top becomes the bottom edge,
161 | * and vice versa.
162 | */
163 | exports.verticalFlip = function() {
164 | var image = new Image(imageData)
165 | image.verticalFlip();
166 | return image.executeTransforms(outputEncoding);
167 | }
168 |
169 | /**
170 | */
171 | exports.imFeelingLucky = function() {
172 | throw new Error("not implemented");
173 | }
174 |
--------------------------------------------------------------------------------
/lib/google/appengine/api/mail.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Sends email on behalf of application.
3 | *
4 | * Provides functions for application developers to provide email services
5 | * for their applications. Also provides a few utility methods.
6 | */
7 |
8 | var MailServiceFactory = Packages.com.google.appengine.api.mail.MailServiceFactory,
9 | Mail = exports.Mail = MailServiceFactory.getMailService(),
10 | Message = exports.Message = Packages.com.google.appengine.api.mail.MailService.Message;
11 |
12 | /**
13 | * An instance of the EmailMessage class represents an email message, and
14 | * includes a method to send the message using the App Engine Mail service.
15 | *
16 | * @constructor
17 | * @param {Object} fields Default values for the email message.
18 | */
19 | var EmailMessage = exports.EmailMessage = function (fields) {
20 | for (var i in fields) this[i] = fields[i];
21 | }
22 |
23 | /**
24 | * Sends the email message.
25 | */
26 | EmailMessage.prototype.send = function () {
27 | var message = new Message();
28 |
29 | message.setSender(this.sender);
30 | message.setTo(this.to);
31 | message.setSubject(this.subject);
32 |
33 | if (this.cc) message.setCc(this.cc);
34 | if (this.bcc) message.setBcc(this.bcc);
35 | if (this.body) message.setTextBody(this.body);
36 | if (this.html) message.setHtmlBody(this.html);
37 |
38 | Mail.send(message);
39 | }
40 |
41 | EmailMessage.prototype.toString = function () {
42 | return this.body || this.html;
43 | }
44 |
45 | /**
46 | * Determine if email is invalid.
47 | * @param {String} emailAddress Email address to check.
48 | * @returns {boolean} True if the email address is valid.
49 | */
50 | exports.isEmailValid = function (emailAddress) {
51 | // FIXME: implement me!
52 | return true;
53 | }
54 |
55 | /**
56 | * Creates and sends a single email message. sender, to, subject, and body are
57 | * required fields of the message. Additional fields can be specified as
58 | * keyword arguments. For the list of possible fields, Email Message Fields.
59 | */
60 | exports.sendMail = function (from, to, subject, body, fields) {
61 | var message = new Message(from, to, subject, body);
62 | Mail.send(message);
63 | }
64 |
65 |
--------------------------------------------------------------------------------
/lib/google/appengine/api/memcache.js:
--------------------------------------------------------------------------------
1 | var jmemcache = Packages.com.google.appengine.api.memcache,
2 | jservice = jmemcache.MemcacheServiceFactory.getMemcacheService(),
3 | Expiration = jmemcache.Expiration,
4 | JSetPolicy = jmemcache.MemcacheService.SetPolicy,
5 | SET_ALWAYS = JSetPolicy.SET_ALWAYS,
6 | ADD_ONLY_IF_NOT_PRESENT = JSetPolicy.ADD_ONLY_IF_NOT_PRESENT;
7 |
8 | var JArrayList = java.util.ArrayList,
9 | JMap = java.util.HashMap;
10 |
11 | /**
12 | * Sets a key's value, regardless of previous contents in cache.
13 | */
14 | exports.set = function(key, value, time, minCompressLen, namespace) {
15 | var expiration = time ? Expiration.byDeltaSeconds(time) : null;
16 | jservice.put(key, JSON.stringify(value), expiration, SET_ALWAYS);
17 | }
18 |
19 | /**
20 | * Set multiple keys' values at once. Reduces the network latency of doing many
21 | * requests in serial.
22 | */
23 | exports.setMulti = exports.setAll = function(mapping, time, keyPrefix, minCompressLen, namespace) {
24 | var expiration = time ? Expiration.byDeltaSeconds(time) : null;
25 | var values = new JMap();
26 | for (var key in mapping) values.put(key, JSON.stringify(mapping[key]));
27 | jservice.putAll(values, time, SET_ALWAYS);
28 | }
29 |
30 | exports.get = function(key, namespace) {
31 | return JSON.parse(String(jservice.get(key)));
32 | }
33 |
34 | /**
35 | * Looks up multiple keys from memcache in one operation. This is the recommended
36 | * way to do bulk loads.
37 | * The returned value is a dictionary of the keys and values that were present
38 | * in memcache. Even if the key_prefix was specified, that key_prefix won't be
39 | * on the keys in the returned dictionary.
40 | */
41 | exports.getMulti = exports.getAll = function(keys, keyPrefix, namespace) {
42 | var list = new JArrayList(keys.length);
43 | for (var i = 0; i < keys.length; i++) list.add(keys[i]);
44 | var objs = jservice.getAll(list);
45 |
46 | var dict = {};
47 | for (var obj in Iterator(objs.entrySet()))
48 | dict[obj.getKey()] = JSON.parse(String(obj.getValue()));
49 |
50 | return dict;
51 | }
52 |
53 | /**
54 | * Deletes a key from memcache.
55 | */
56 | exports.remove = exports.DELETE = function(key, seconds, namespace) {
57 | if (seconds != undefined) {
58 | return jservice["delete"](key, seconds * 1000);
59 | } else {
60 | return jservice["delete"](key);
61 | }
62 | }
63 |
64 | /**
65 | * Deletes a key from memcache.
66 | */
67 | exports.removeAll = exports.DELETEALL = function(keys, seconds, namespace) {
68 | var list = new JArrayList(keys.length);
69 | for (var i = 0; i < keys.length; i++) list.add(keys[i]);
70 | return jservice["delete"](list, seconds * 1000);
71 | }
72 |
73 | /**
74 | * Sets a key's value, if and only if the item is not already in memcache.
75 | */
76 | exports.add = function(key, value, time, minCompressLen, namespace) {
77 | var expiration = time ? Expiration.byDeltaSeconds(time) : null;
78 | jservice.put(key, JSON.stringify(value), expiration, ADD_ONLY_IF_NOT_PRESENT);
79 | }
80 |
81 | /**
82 | * Adds multiple keys' values at once. Reduces the network latency of doing many
83 | * requests in serial.
84 | */
85 | exports.addMulti = exports.addAll = function(mapping, time, keyPrefix, minCompressLen, namespace) {
86 | var expiration = time ? Expiration.byDeltaSeconds(time) : null;
87 | var values = new JMap();
88 | for (var key in mapping) values.put(key, JSON.stringify(mapping[key]));
89 | jservice.putAll(values, time, ADD_ONLY_IF_NOT_PRESENT);
90 | }
91 |
92 | /**
93 | * Atomically increments a key's value. Internally, the value is a unsigned
94 | * 64-bit integer. Memcache doesn't check 64-bit overflows. The value, if too
95 | * large, will wrap around.
96 | * If the key does not yet exist in the cache and you specify an initialValue,
97 | * the key's value will be set to this initial value and then incremented.
98 | * If the key does not exist and no initialValue is specified, the key's
99 | * value will not be set.
100 | */
101 | exports.incr = function(key, delta, namespace, initialValue) {
102 | if (initialValue) {
103 | jservice.increment(key, delta || 1, initialValue);
104 | } else {
105 | jservice.increment(key, delta || 1);
106 | }
107 | }
108 |
109 | /**
110 | * Atomically decrements a key's value. Internally, the value is a unsigned
111 | * 64-bit integer. Memcache doesn't check 64-bit overflows. The value, if too
112 | * large, will wrap around.
113 | * If the key does not yet exist in the cache and you specify an initialValue,
114 | * the key's value will be set to this initial value and then decremented.
115 | * If the key does not exist and no initialValue is specified, the key's
116 | * value will not be set.
117 | */
118 | exports.decr = function(key, delta, namespace, initialValue) {
119 | if (initialValue) {
120 | jservice.increment(key, -(delta || 1), initialValue);
121 | } else {
122 | jservice.increment(key, -(delta || 1));
123 | }
124 | }
125 |
126 | /**
127 | * Increments or decrements multiple keys with integer values in a single
128 | * service call. Each key can have a separate offset. The offset can be positive
129 | * or negative.
130 | * Applying an offset to a single key is atomic. Applying an offset to multiple
131 | * keys may succeed for some keys and fail for others.
132 | *
133 | * mapping
134 | * Dictionary of keys to offsets. An offset can be a positive or negative
135 | * integer to be added to the key's value.
136 | * key_prefix
137 | * Prefix for to prepend to all keys.
138 | */
139 | exports.offsetMulti = function(mapping, key_prefix, namespace, initialValue) {
140 | var offsets = new JMap();
141 | for (var key in mapping) offsets.put(key, mapping[key]);
142 |
143 | if (initialValue) {
144 | jservice.incrementAll(offsets, initialValue);
145 | } else {
146 | jservice.incrementAll(offsets, -(delta || 1));
147 | }
148 | }
149 |
150 | // TODO: Java incrementAll API (many keys, one delta)
151 | exports.offsetAll = function(keys, delta, namespace, initialValue) {
152 | }
153 |
154 | /**
155 | * Deletes everything in memcache.
156 | */
157 | exports.flushAll = exports.clearAll = function() {
158 | jservice.clearAll();
159 | }
160 |
161 | /**
162 | * A client for communicating with the Memcache service.
163 | * Provides compatibility with the Python Memcached API, not implemented.
164 | *
165 | * @constructor
166 | */
167 | var Client = exports.Client = function() {
168 | return exports;
169 | }
170 |
--------------------------------------------------------------------------------
/lib/google/appengine/api/namespace-manager.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Control the namespacing system used by various APIs.
3 | *
4 | * A namespace may be specified in various API calls exemplified
5 | * by the datastore and memcache interfaces. The default can be
6 | * specified using this module.
7 | */
8 |
9 | var JNamespaceManager = Packages.com.google.appengine.api.NamespaceManager;
10 |
11 | /**
12 | * Raised by the validateNamespace() method when the namespace
13 | * string is not valid. To be valid, namespace strings must match
14 | * the regular expression ([0-9A-Za-z._-]{0,100}).
15 | */
16 | exports.BadValueError = java.lang.IllegalArgumentException;
17 |
18 | /**
19 | * Set the default namespace for the current HTTP request.
20 | * @param {string} namespace A string naming the new namespace to use. A value of undefined will unset the default namespace value.
21 | */
22 | exports.setNamespace = function (namespace) {
23 | JNamespaceManager.set(namespace);
24 | }
25 |
26 | /**
27 | * Get the the current default namespace or ('') namespace if unset.
28 | */
29 | exports.getNamespace = function () {
30 | return JNamespaceManager.get();
31 | }
32 |
33 | /**
34 | * Returns the Google Apps domain referring this request or
35 | * otherwise the empty string ("").
36 | */
37 | exports.getGoogleAppsNamespace = function () {
38 | return JNamespaceManager.getGoogleAppsNamespace();
39 | }
40 |
41 | /**
42 | * Set the default namespace to the Google Apps domain referring
43 | * this request.
44 | *
45 | * Calling this function will set the default namespace to the
46 | * Google Apps domain that was used to create the url used for
47 | * this request and only for the current request and only if the
48 | * current default namespace is unset.
49 | */
50 | exports.enableRequestNamespace = function () {
51 | JNamespaceManager.set(JNamespaceManager.getGoogleAppsNamespace());
52 | }
53 |
54 | /**
55 | * Raises an exception if value is not a valid Namespace string.
56 | * A namespace must match the regular expression
57 | * ([0-9A-Za-z._-]{0,100}).
58 | * @param {string} value The value to validate.
59 | * @throws {java.lang.IllegalArgumentException}
60 | */
61 | exports.validateNamespace = function (value) {
62 | JNamespaceManager.validateNamespace(value);
63 | }
64 |
--------------------------------------------------------------------------------
/lib/google/appengine/api/oauth.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview OAuth API.
3 | * A service that enables App Engine apps to validate OAuth requests.
4 | */
5 |
6 | var JOAUTH = Packages.com.google.appengine.api.oauth,
7 | JOAuthServiceFactory = JOAUTH.OAuthServiceFactory;
8 |
9 | var User = require("google/appengine/api/users").User;
10 |
11 | /** Base error type for invalid OAuth requests. */
12 | exports.OAuthRequestError = JOAUTH.OAuthRequestException;
13 |
14 | /** Thrown if there was a problem communicating with the OAuth service. */
15 | exports.OAuthServiceFailureError = JOAUTH.OAuthServiceFailureException;
16 |
17 | /**
18 | * Returns the User on whose behalf the request was made.
19 | *
20 | * @return {User} The current user.
21 | * @throws {OAuthRequestError} The request was not a valid OAuth request.
22 | * @throws {OAuthServiceFailureError} An unknown error occurred.
23 | */
24 | exports.getCurrentUser = function () {
25 | return User.fromJavaUser(OAuthServiceFactory.getOAuthService().getCurrentUser());
26 | }
27 |
28 | /**
29 | * Returns true if the User on whose behalf the request was made is an admin.
30 | *
31 | * @return {boolean} Is the user admin?
32 | * @throws {OAuthRequestError} The request was not a valid OAuth request.
33 | * @throws {OAuthServiceFailureError} An unknown error occurred.
34 | */
35 | exports.isCurrentUserAdmin = function () {
36 | return OAuthServiceFactory.getOAuthService().isUserAdmin();
37 | }
38 |
39 | /**
40 | * Returns the value of the 'oauth_consumer_key' parameter from the request.
41 | *
42 | * @returns {string} The value of the 'oauth_consumer_key' parameter from the request,
43 | * an identifier for the consumer that signed the request.
44 | * @throws {OAuthRequestError} The request was not a valid OAuth request.
45 | * @throws {OAuthServiceFailureError} An unknown error occurred.
46 | */
47 | exports.getOAuthConsumerKey = function () {
48 | return OAuthServiceFactory.getOAuthService().getOAuthConsumerKey();
49 | }
50 |
--------------------------------------------------------------------------------
/lib/google/appengine/api/quota.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Access to quota usage for this application.
3 | */
4 |
5 | var JQuotaServiceFactory = Packages.google.appengine.api.quota.QuotaServiceFactory,
6 | jservice = JQuotaServiceFactory.getQuotaService;
7 |
8 | /**
9 | * Get the amount of CPU used so far for the current request.
10 | *
11 | * Returns the number of megacycles used so far for the current
12 | * request. Does not include CPU used by API calls.
13 | *
14 | * The unit the duration is measured is Megacycles. If all instructions were to
15 | * be executed sequentially on a standard 1.2 GHz 64-bit x86 CPU, 1200
16 | * megacycles would equate to one second physical time elapsed.
17 | *
18 | * Does nothing when used in the dev_appserver.
19 | */
20 | exports.getRequestCpuUsage = function () {
21 | return Number(jservice.getCpuTimeInMegaCycles());
22 | }
23 |
24 | /**
25 | * Get the amount of CPU used so far by API calls during the current request.
26 | *
27 | * Returns the number of megacycles used so far by API calls for the current
28 | * request. Does not include CPU used by code in the request itself.
29 | *
30 | * Does nothing when used in the dev_appserver.
31 | */
32 | exports.getRequestCpuApiUsage = function () {
33 | return Number(jservice.getApiTimeInMegaCycles());
34 | }
35 |
36 | /**
37 | * Convert an input value in megacycles to CPU-seconds.
38 | *
39 | * Returns a Number (float) representing the CPU-seconds the input megacycle value
40 | * converts to.
41 | */
42 | exports.megacyclesToCpuSeconds = function (msycles) {
43 | return Number(jservice.convertMegacyclesToCpuSeconds(mcycles));
44 | }
45 |
46 | /**
47 | * Convert an input value in CPU-seconds to megacycles.
48 | *
49 | * Returns an integer representing the megacycles the input CPU-seconds value
50 | * converts to.
51 | */
52 | exports.cpuSecondsToMegacycles = function (cpu) {
53 | return Number(jservice.convertCpuSecondsToMegacycles(cpu));
54 | }
55 |
--------------------------------------------------------------------------------
/lib/google/appengine/api/taskqueue.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Task Queue API.
3 | *
4 | * Enables an application to queue background work for itself. Work is done through
5 | * webhooks that process tasks pushed from a queue. Tasks will execute in
6 | * best-effort order of ETA. Webhooks that fail will cause tasks to be retried at a
7 | * later time. Multiple queues may exist with independent throttling controls.
8 | *
9 | * Webhook URLs may be specified directly for Tasks, or the default URL scheme
10 | * may be used, which will translate Task names into URLs relative to a Queue's
11 | * base path. A default queue is also provided for simple usage.
12 | */
13 |
14 | var JQueue = Packages.com.google.appengine.api.taskqueue.Queue,
15 | JQueueFactory = Packages.com.google.appengine.api.taskqueue.QueueFactory,
16 | JTaskOptions = Packages.com.google.appengine.api.taskqueue.TaskOptions,
17 | JTaskOptionsMethod = JTaskOptions.Method,
18 | JTaskOptionsBuilder = JTaskOptions.Builder;
19 |
20 | var HTTP_METHODS = {
21 | "DELETE": JTaskOptionsMethod.DELETE,
22 | "GET": JTaskOptionsMethod.GET,
23 | "HEAD": JTaskOptionsMethod.HEAD,
24 | "POST": JTaskOptionsMethod.POST,
25 | "PUT": JTaskOptionsMethod.PUT
26 | }
27 |
28 | var jdefaultQueue = JQueueFactory.getDefaultQueue();
29 |
30 | var defineError = require("google/appengine/utils").defineError;
31 |
32 | /** The queue specified is unknown. */
33 | exports.UnknownQueueError = defineError("UnknownQueueError");
34 |
35 | /**
36 | * There was a transient error while accessing the queue.
37 | * Please Try again later.
38 | */
39 | exports.TransientError = defineError("TransientError");
40 |
41 | /**
42 | * Instantiates a Task object which will describe a unit of offline work.
43 | * The instance may be inserted into a queue, so that it will be executed
44 | * asynchronously by App Engine.
45 | *
46 | * All arguments are optional.
47 | *
48 | * Arguments:
49 | * - payload
50 | * - countdown
51 | * - eta
52 | * - headers
53 | * - method
54 | * - name
55 | * - params
56 | * - url
57 | *
58 | * @constructor
59 | */
60 | var Task = exports.Task = function(args) {
61 | var o = this.jtaskOptions = JTaskOptionsBuilder.withDefaults();
62 |
63 | if (args.url) o.url(args.url);
64 | if (args.name) o.taskName(args.name);
65 | if (args.eta) o.etaMillis(args.eta);
66 | if (args.method) o.method(HTTP_METHODS[args.method]);
67 |
68 | if (args.params) {
69 | for (var paramName in args.params) {
70 | o.param(paramName, args.params[paramName]);
71 | }
72 | }
73 | }
74 |
75 | /**
76 | * Adds this Task to a queue. The queue is specified by name. A single Task
77 | * instance may only be added to one queue.
78 | */
79 | Task.prototype.add = function(queueName) {
80 | var jqueue = queueName ? JQueueFactory.getQueue(queueName) : jdefaultQueue;
81 | jqueue.add(this.jtaskOptions);
82 | }
83 |
84 | /**
85 | * Instantiates a Queue object which corresponds to either the default queue
86 | * (automatically available to all applications) or a user-created queue
87 | * defined in queue.yaml. Once instantiated, a Queue object may be used to
88 | * insert new Tasks into the system for offline execution.
89 | *
90 | * @constructor
91 | */
92 | var Queue = exports.Queue = function(name) {
93 | this.name = name;
94 |
95 | if (name) {
96 | this._jqueue = JQueueFactory.getQueue(name);
97 | } else {
98 | this._jqueue = jdefaultQueue;
99 | this.name = "default";
100 | }
101 | }
102 |
103 | /**
104 | * Add a Task to this Queue.
105 | */
106 | Queue.prototype.add = function(task) {
107 | task.add(this)
108 | }
109 |
110 | /**
111 | * Convenience method will create a Task and add it to the default queue.
112 | *
113 | * Args:
114 | * *args, **kwargs: Passed to the Task constructor.
115 | *
116 | * Returns:
117 | * The Task that was added to the queue.
118 | */
119 | exports.add = function(args) {
120 | var task = new Task(args);
121 | task.add();
122 | }
123 |
--------------------------------------------------------------------------------
/lib/google/appengine/api/urlfetch.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview A conversion of the Python urlfetch api to JavaScript.
3 | * Implemented on top of the low level Java API.
4 | *
5 | * http://code.google.com/appengine/docs/python/urlfetch/
6 | * http://code.google.com/appengine/docs/java/javadoc/com/google/appengine/api/urlfetch/package-summary.html
7 | */
8 |
9 | var JURLFetch = exports.URLFetch = Packages.com.google.appengine.api.urlfetch;
10 |
11 | var JURLFetchService = JURLFetch.URLFetchServiceFactory.getURLFetchService(),
12 | JURL = Packages.java.net.URL,
13 | JHTTPRequest = JURLFetch.HTTPRequest,
14 | JHTTPResponse = JURLFetch.HTTPResponse,
15 | JHTTPMethod = JURLFetch.HTTPMethod,
16 | JHTTPHeader = JURLFetch.HTTPHeader,
17 | JString = Packages.java.lang.String,
18 | JTimeUnit = Packages.java.util.concurrent.TimeUnit;
19 |
20 | var ByteString = require("binary").ByteString;
21 |
22 | /** @enum */
23 | var HTTP_METHODS = {
24 | "DELETE": JHTTPMethod.DELETE,
25 | "GET": JHTTPMethod.GET,
26 | "HEAD": JHTTPMethod.HEAD,
27 | "POST": JHTTPMethod.POST,
28 | "PUT": JHTTPMethod.PUT
29 | }
30 |
31 | /**
32 | * The fetch() function makes a synchronous request to fetch a URL. This
33 | * function is provided by the google.appengine.api.urlfetch package.
34 | *
35 | * The fetch() function returns an object containing the details of the response
36 | * returned by the URL's server. This object has several attributes:
37 | *
38 | * - content {ByteString: http://ringojs.org/api/master/binary#ByteString}
39 | * - statusCode
40 | * - headers
41 | *
42 | * Please note that content is a ByteString, you may use content.decodeToString()
43 | * to convert it to a String.
44 | *
45 | * @param {string} url The URL of the request.
46 | * @param {ByteString=} payload The optional payload for POST/PUT requests.
47 | * @returns {} The HTTP response.
48 | */
49 | exports.fetch = function (url, payload, method, headers, allowTruncated, followRedirects, deadline) {
50 | var request = createRequest(url, payload, method, headers, allowTruncated, followRedirects, deadline),
51 | response = JURLFetchService.fetch(request);
52 | return parseResponse(response);
53 | }
54 |
55 | /**
56 | * callback is not supported at the moment.
57 | */
58 | exports.createRPC = function (deadline, callback) {
59 | return new RPC(deadline, callback);
60 | }
61 |
62 | /**
63 | *
64 | */
65 | exports.makeFetchCall = function (rpc, url, payload, method, headers, allowTruncated, followRedirects, deadline) {
66 | var request = createRequest(url, payload, method, headers, allowTruncated, followRedirects, deadline);
67 | rpc._future = JURLFetchService.fetchAsync(request);
68 | return rpc;
69 | }
70 |
71 | /**
72 | * @constructor
73 | */
74 | var RPC = function (deadline, callback) {
75 | this.deadline = deadline;
76 | }
77 |
78 | /**
79 | *
80 | */
81 | RPC.prototype.wait = function () {
82 | var response = this.deadline ? this._future.get(this.deadline, JTimeUnit.SECONDS) : this._future.get();
83 | if (this.callback) {
84 | this.callback.call();
85 | }
86 | }
87 |
88 | /**
89 | *
90 | */
91 | RPC.prototype.checkSuccess = function () {
92 | this.wait();
93 | }
94 |
95 | /**
96 | *
97 | */
98 | RPC.prototype.getResult = function () {
99 | var response = this.deadline ? this._future.get(this.deadline, JTimeUnit.SECONDS) : this._future.get();
100 | return parseResponse(response);
101 | }
102 |
103 | var createRequest = function (url, payload, method, headers, allowTruncated, followRedirects, deadline) {
104 | var request = new JHTTPRequest(JURL(url), HTTP_METHODS[method || "GET"]);
105 |
106 | if (headers) {
107 | for (var i in headers) request.addHeader(new JHTTPHeader(i, headers[i]));
108 | }
109 |
110 | if (payload) {
111 | if (typeof(payload) == "string") {
112 | request.setPayload(new JString(payload).getBytes("UTF-8"));
113 | } else {
114 | // request.setPayload(payload._bytes); // FIXME: implementation specific.
115 | request.setPayload(payload);
116 | }
117 | }
118 |
119 | return request;
120 | }
121 |
122 | var parseResponse = function (response) {
123 | var b = new ByteString.wrap(response.getContent());
124 |
125 | var headers = {};
126 | for (var h in Iterator(response.getHeaders())) {
127 | headers[String(h.getName())] = String(h.getValue());
128 | }
129 |
130 | return {
131 | content: b,
132 | finalUrl: String(response.getFinalUrl()),
133 | statusCode: response.getResponseCode(),
134 | headers: headers
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/lib/google/appengine/api/users.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Users API.
3 | *
4 | * http://code.google.com/appengine/docs/java/users/
5 | * http://code.google.com/appengine/docs/python/users/
6 | */
7 |
8 | var JHashSet = Packages.java.util.HashSet,
9 | JUserServiceFactory = Packages.com.google.appengine.api.users.UserServiceFactory,
10 | JUser = Packages.com.google.appengine.api.users.User,
11 | jservice = JUserServiceFactory.getUserService();
12 |
13 | var endsWith = require("ringo/utils/strings").endsWith;
14 |
15 | var defineError = require("google/appengine/utils").defineError;
16 | exports.UserNotFoundError = defineError("UserNotFoundError");
17 | exports.RedirectTooLongError = defineError("RedirectTooLongError");
18 |
19 | /**
20 | * A user.
21 | *
22 | * We provide the email address, nickname, auth domain, and id for a user.
23 | *
24 | * A nickname is a human-readable string which uniquely identifies a Google
25 | * user, akin to a username. It will be an email address for some users, but
26 | * not all.
27 | *
28 | * A user could be a Google Accounts user or a federated login user.
29 | *
30 | * federatedIdentity and federatedProvider are only avaliable for
31 | * federated users.
32 | *
33 | * @constructor
34 | * @param {string=} email An optional string of the user's email address. It defaults to the current user's email address.
35 | * @param {string=} authDomain
36 | * @param {string=} userId
37 | * @param {string=} federatedIdentity Federated identity of user. It defaults to the current user's federated identity.
38 | * @param {string=} federatedProvider Federated provider url of user.
39 | * @return {User} The user.
40 | */
41 | var User = exports.User = function (email, authDomain, userId, federatedIdentity, federatedProvider) {
42 | if (email) {
43 | this.email = email;
44 | this.authDomain = authDomain;
45 | this.userId = userId;
46 | this.federatedIdentity = federatedIdentity;
47 | this.federatedProvider = federatedProvider;
48 | } else {
49 | var user = jservice.getCurrentUser();
50 |
51 | if (user) {
52 | this.email = user.getEmail();
53 | this.authDomain = user.getAuthDomain();
54 | this.nickname = user.getNickname();
55 | this.userId = user.getUserId();
56 | this.federatedIdentity = user.getFederatedIdentity();
57 | } else {
58 | throw new exports.UserNotFoundError();
59 | }
60 | }
61 | }
62 |
63 | // Create a user object from a Java object.
64 | User.fromJavaUser = function (juser) {
65 | var user = new User();
66 | user.email = juser.getEmail();
67 | user.nickname = juser.getNickname();
68 | user.userId = juser.getUserId();
69 | return user;
70 | }
71 |
72 | User.prototype.toJavaUser = function () {
73 | return new JUser(this.email, this.authDomain, this.userId);
74 | }
75 |
76 | /**
77 | * Returns a URL that, when visited, will prompt the user to sign in using a
78 | * Google account, then redirect the user back to the URL given as dest_url.
79 | * This URL is suitable for links, buttons and redirects.
80 | *
81 | * @param {string} destURL String that is the desired final destination URL for the user
82 | * once login is complete. If 'destURL' does not have a host
83 | * specified, we will use the host from the current request.
84 | * @param {string} authDomain Ignored.
85 | * @param {string} federated_identity FederatedIdentity is used to trigger OpenId Login
86 | * flow, an empty value will trigger Google OpenID Login by default.
87 | * @return {string} Login URL as a string. If federated_identity is set, this
88 | * will be a federated login using the specified identity. If not, this will use Google Accounts.
89 | */
90 | exports.createLoginURL = function (options) {
91 | if (typeof(options) == "string") {
92 | return jservice.createLoginURL(/* destURL */options);
93 | } else {
94 | if (options.federatedIdentity) {
95 | return jservice.createLoginURL(options.destURL, options.authDomain, options.federatedIdentity, new JHashSet());
96 | } else {
97 | return jservice.createLoginURL(options.destURL);
98 | }
99 | }
100 | }
101 |
102 | /**
103 | * Computes the logout URL for this request and specified destination URL, for
104 | * both federated login App and Google Accounts App.
105 | *
106 | * @param {string} String that is the desired final destination URL for the user
107 | * once logout is complete. If 'destURL' does not have a host
108 | * specified, we will use the host from the current request.
109 | * @return {string} Logout URL as a string.
110 | */
111 | exports.createLogoutURL = function (destURL) {
112 | return jservice.createLogoutURL(destURL);
113 | }
114 |
115 | /**
116 | * Returns the User object for the current user (the user who made the request
117 | * being processed) if the user is signed in, or None if the user is not
118 | * signed in.
119 | */
120 | exports.getCurrentUser = function () {
121 | var currentUser = jservice.getCurrentUser();
122 |
123 | if (currentUser) {
124 | var user = new User(currentUser.getEmail());
125 | user.nickname = currentUser.getNickname();
126 | user.userId = currentUser.getUserId();
127 |
128 | return user;
129 | } else {
130 | return null;
131 | }
132 | }
133 |
134 | /**
135 | * Return true if the user making this request is an admin for this
136 | * application, false otherwise.
137 | *
138 | * We specifically make this a separate function, and not a member function of
139 | * the User class, because admin status is not persisted in the datastore. It
140 | * only exists for the user making this request right now.
141 | *
142 | * @return {boolean} True if the user is admin.
143 | */
144 | exports.isCurrentUserAdmin = function () {
145 | return jservice.isUserAdmin();
146 | }
147 |
--------------------------------------------------------------------------------
/lib/google/appengine/api/utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview System properties. Based on the Java API:
3 | * @see http://code.google.com/appengine/docs/java/javadoc/com/google/appengine/api/utils/SystemProperty.html
4 | * A Python API version will be added in a future version:
5 | * @see http://code.google.com/appengine/docs/python/runtime.html#The_Environment
6 | */
7 |
8 | // TODO: implement Python API.
9 |
10 | var JSystemProperty = Packages.com.google.appengine.api.utils.SystemProperty;
11 |
12 | /**
13 | * Custom API to access system properties, based on the Java API.
14 | */
15 | var SystemProperties = exports.SystemProperties = {};
16 |
17 | Object.defineProperty(SystemProperties, "applicationId", {
18 | get: function () {
19 | return JSystemProperty.applicationId.get();
20 | }
21 | });
22 |
23 | Object.defineProperty(SystemProperties, "applicationVersion", {
24 | get: function () {
25 | return JSystemProperty.applicationVersion.get();
26 | }
27 | });
28 |
29 | /**
30 | * The current executing environment.
31 | * Has the values "Production" and "Development".
32 | *
33 | * Example:
34 | * var enviroment = require("google/appengine/api/utils").SystemProperties.environment;
35 | * Alternatives:
36 | * var environment = require("ringo/engine").properties["com.google.appengine.runtime.environment"];
37 | * var environment = java.lang.System.getProperty("com.google.appengine.runtime.environment"))
38 | */
39 | Object.defineProperty(SystemProperties, "environment", {
40 | get: function () {
41 | return JSystemProperty.environment.value();
42 | },
43 | set: function (e) {
44 | JSystemProperty.environment.set(e);
45 | }
46 | });
47 |
48 | Object.defineProperty(SystemProperties, "version", {
49 | get: function () {
50 | return JSystemProperty.version.get();
51 | }
52 | });
53 |
--------------------------------------------------------------------------------
/lib/google/appengine/api/xmpp.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview An App Engine application can send and receive instant messages to and from
3 | * any XMPP-compatible instant messaging service, such as Google Talk. An app
4 | * can send and receive chat messages, send chat invites, and request status
5 | * information. Incoming XMPP messages are handled by request handlers, similar
6 | * to web requests.
7 | *
8 | * Some possible uses of instant messages include automated chat participants
9 | * ("chat bots"), instant notifications, and chat interfaces to services. A
10 | * rich client with a connection to an XMPP server (such as Google Talk) can
11 | * use XMPP to interact with an App Engine application in real time, including
12 | * receiving messages initiated by the app. (Note that such a client using
13 | * Google Talk must use the user's password to make an XMPP connection, and
14 | * cannot use a Google Accounts cookie.)
15 | *
16 | * Example usage:
17 | *
18 | * Map the following JSGI app at ah/xmpp/message/chat
19 | *
20 | * var Request = require("nitro/request").Request,
21 | * Message = require("google/appengine/api/xmpp").Message;
22 | *
23 | * exports.GET = exports.POST = function(env) {
24 | * var msg = new Message(env);
25 | * msg.reply("Hello, you said: " + msg.body);
26 | * return {status: 200};
27 | * }
28 | *
29 | * Due to appengine restrictions you have to rewrite paths starting with _ah to
30 | * ah, and put your JSGI app in ah/
31 | */
32 | var JXMPP = Packages.com.google.appengine.api.xmpp,
33 | JXMPPServiceFactory = JXMPP.XMPPServiceFactory,
34 | JMessageBuilder = JXMPP.MessageBuilder,
35 | JMessageType = JXMPP.MessageType,
36 | JArrayList = java.util.ArrayList,
37 | JJID = JXMPP.JID;
38 |
39 | var jservice = JXMPPServiceFactory.getXMPPService();
40 |
41 | /** @const */ exports.MESSAGE_TYPE_NONE = "";
42 | /** @const */ exports.MESSAGE_TYPE_CHAT = JMessageType.CHAT;
43 | /** @const */ exports.MESSAGE_TYPE_ERROR = JMessageType.ERROR;
44 | /** @const */ exports.MESSAGE_TYPE_GROUPCHAT = JMessageType.GROUPCHAT;
45 | /** @const */ exports.MESSAGE_TYPE_HEADLINE = JMessageType.HEADLINE;
46 | /** @const */ exports.MESSAGE_TYPE_NORMAL = JMessageType.NORMAL;
47 |
48 | /**
49 | * Queries the presence status of a Google Talk user.
50 | */
51 | exports.getPresence = function(jid, fromJid) {
52 | if (fromJid) {
53 | return jservice.getPresence(new JJID(jid), new JJID(fromJid)).isAvailable();
54 | } else {
55 | return jservice.getPresence(new JJID(jid)).isAvailable();
56 | }
57 | }
58 |
59 | /**
60 | * Sends an invitation to a user to chat.
61 | */
62 | exports.sendInvite = function(jid, fromJid) {
63 | if (fromJid) {
64 | jservice.sendInvitation(new JJID(jid), new JJID(fromJid));
65 | } else {
66 | jservice.sendInvitation(new JJID(jid));
67 | }
68 | }
69 |
70 | /**
71 | * Sends a chat message to a list of JIDs.
72 | */
73 | exports.sendMessage = function(jids, body, fromJid, messageType, rawXML) {
74 | if (typeof(jids) == "string") jids = [jids];
75 | jids = jids.map(function(jid) { return new JJID(jid); });
76 |
77 | var mb = new JMessageBuilder().withBody(body).withRecipientJids(jids).withMessageType(messageType || exports.MESSAGE_TYPE_CHAT);
78 | if (fromJid) mb = mb.withFromJid(new JJID(fromJid));
79 | if (!!rawXML) mb = mb.asXml(true);
80 | var response = jservice.sendMessage(mb.build());
81 |
82 | }
83 |
84 | /**
85 | * Encapsulates an XMPP message received by the application.
86 | * Constructs a new XMPP Message from an HTTP request.
87 | *
88 | * Different from the Python API: The JSGI env is passed.
89 | *
90 | * @constructor
91 | */
92 | var Message = exports.Message = function(env) {
93 | var jmsg = jservice.parseMessage(env["jack.servlet_request"]);
94 | this.sender = String(jmsg.getFromJid().getId());
95 | var to = [],
96 | rjids = jmsg.getRecipientJids();
97 | for (var i = 0; i < rjids.length; i++) {
98 | to.push(String(rjids[i].getId()));
99 | }
100 | this.to = to;
101 | this.body = String(jmsg.getBody());
102 | }
103 |
104 | /**
105 | * Convenience function to reply to a message.
106 | *
107 | * Args:
108 | * body: str: the body of the message
109 | * messageType, rawXML: as per exports.sendMessage.
110 | * sendMessage: used for testing.
111 | */
112 | Message.prototype.reply = function(body, messageType, rawXML, sendMessage) {
113 | if (!sendMessage) sendMessage = exports.sendMessage;
114 | sendMessage(this.sender, body, this.to, messageType, rawXML);
115 | }
116 |
--------------------------------------------------------------------------------
/lib/google/appengine/ext/blobstore.js:
--------------------------------------------------------------------------------
1 | var db = require("google/appengine/ext/db"),
2 | Query = db.Query,
3 | Property = db.Property,
4 | blobstore = require("google/appengine/api/blobstore"),
5 | utils = require("google/appengine/utils");
6 |
7 | var BLOB_INFO_KIND = blobstore.BLOB_INFO_KIND,
8 | BLOB_KEY_HEADER = blobstore.BLOB_KEY_HEADER;
9 |
10 | /**
11 | * Information about blobs in Blobstore.
12 | *
13 | * This is a db.Model-like class that contains information about blobs stored
14 | * by an application. Like db.Model, this class is backed by an Datastore
15 | * entity, however, BlobInfo instances are read-only and have a much more
16 | * limited interface.
17 | *
18 | * Each BlobInfo has a key of type BlobKey associated with it. This key is
19 | * specific to the Blobstore API and is not compatible with db.get. The key
20 | * can be used for quick lookup by passing it to BlobInfo.get. This
21 | * key converts easily to a string, which is web safe and can be embedded
22 | * in URLs.
23 | *
24 | * Properties:
25 | * contentType: Content type of blob.
26 | * creation: Creation date of blob, when it was uploaded.
27 | * filename: Filename user selected from their machine.
28 | * size: Size of uncompressed blob.
29 | *
30 | * All properties are read-only. Attempting to assign a value to a property
31 | * will raise NotImplementedError.
32 | *
33 | * @constructor
34 | */
35 | var BlobInfo = exports.BlobInfo = function (entity_or_blobKey) {
36 | };
37 |
38 | BlobInfo.fromEntity = function (entity) {
39 | return new BlobInfo(entity);
40 | }
41 |
42 | BlobInfo.properties = function () {
43 | return ["contentType", "creation", "filename", "size"];
44 | }
45 |
46 | BlobInfo.kind = function () {
47 | return BLOB_INFO_KIND;
48 | }
49 |
50 | /**
51 | */
52 | BlobInfo.get = function (blobKeys) {
53 | if (!blobKeys) return;
54 |
55 | return db.get(blobKeys);
56 |
57 | if (!objs) return null;
58 |
59 | var kind = this.kind();
60 | var arr = Array.isArray(objs) ? objs : [objs];
61 | for (var i = 0; i < arr.length; i++) {
62 | if (arr[i].constructor.kind() != kind)
63 | throw new KindError("Instance is of kind '" + arr[i].constructor.kind() + "', expected kind is '" + kind + "'");
64 | }
65 |
66 | return objs;
67 | }
68 |
69 | /**
70 | * Get query for all Blobs associated with application.
71 | *
72 | * Returns:
73 | * A db.Query object querying over BlobInfo's datastore kind.
74 | */
75 | BlobInfo.all = function () {
76 | return new Query(this);
77 | }
78 |
79 | /**
80 | */
81 | BlobInfo.prototype.key = function () {
82 | return this.__key__;
83 | }
84 |
85 | /**
86 | */
87 | BlobInfo.prototype.remove = BlobInfo.prototype.DELETE = function () {
88 | blobstore.remove(this.key());
89 | }
90 |
91 | BlobInfo.prototype.toString = function () {
92 | return this.__key__;
93 | }
94 |
95 | /**
96 | * Get a BlobInfo record from blobstore.
97 | *
98 | * Does the same as BlobInfo.get.
99 | */
100 | exports.get = function (blobKey) {
101 | return BlobInfo.get(blobKey);
102 | }
103 |
104 |
105 | /**
106 | * Add references to blobs to domain models using BlobReferenceProperty:
107 | *
108 | * var Picture = db.Model({
109 | * title: new db.StringProperty(),
110 | * image: new blobstore.BlobReferenceProperty(),
111 | * thumbnail: new blobstore.BlobReferenceProperty()
112 | * }
113 | *
114 | * To find the size of a picture using this model:
115 | *
116 | * var picture = Picture.get(picture_key)
117 | * print(picture.image.size);
118 | *
119 | * BlobInfo objects are lazily loaded so iterating over models with
120 | * for BlobKeys is efficient, the following does not need to hit
121 | * Datastore for each image key:
122 | *
123 | * var list = [];
124 | * Picture.all().filter("title =", "").fetch().forEach(function (picture) {
125 | * list.push(picture.image.key());
126 | * }
127 | *
128 | * @constructor
129 | */
130 | var BlobReferenceProperty = exports.BlobReferenceProperty = function (options) {
131 | Property.call(this, options);
132 | // THINK: this.indexed = false;
133 | }
134 |
135 | utils.extend(BlobReferenceProperty, Property);
136 |
137 | BlobReferenceProperty.prototype.init = function (constructor) {
138 | }
139 |
140 | BlobReferenceProperty.prototype.getValueForDatastore = function (obj) {
141 | var blobInfo = obj[this.name];
142 | return blobInfo ? blobInfo.toString() : null;
143 | }
144 |
145 | BlobReferenceProperty.prototype.makeValueFromDatastore = function (value) {
146 | return value ? new BlobInfo(value) : null;
147 | }
148 |
149 | var super_validate = Property.prototype.validate;
150 | BlobReferenceProperty.prototype.validate = function (value) {
151 | if ((typeof(value) == "string") || (value.constructor == BlobKey)) {
152 | value = new BlobInfo(value)
153 | }
154 |
155 | return super_validate.call(this, value);
156 | }
157 |
158 |
159 | /**
160 | * Provides a read-only file-like interface to a blobstore blob.
161 | * UNDER CONSTRUCTION
162 | *
163 | * @constructor
164 | */
165 | var BlobReader = exports.BlobReader = function () {
166 | }
167 |
168 | /**
169 | */
170 | BlobReader.prototype.read = function (size) {
171 | }
172 |
173 | /**
174 | */
175 | BlobReader.prototype.readline = function (size) {
176 | }
177 |
178 | /**
179 | */
180 | BlobReader.prototype.readlines = function (size) {
181 | }
182 |
183 | /**
184 | */
185 | BlobReader.prototype.seek = function (offset, whence) {
186 | }
187 |
188 | /**
189 | */
190 | BlobReader.prototype.tell = function () {
191 | }
192 |
--------------------------------------------------------------------------------
/lib/google/appengine/ext/blobstore/downloadapp.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gmosx/appengine_js/10677f56d8aae95f4c2d7ef4b560f21bc18913eb/lib/google/appengine/ext/blobstore/downloadapp.js
--------------------------------------------------------------------------------
/lib/google/appengine/ext/blobstore/uploadapp.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gmosx/appengine_js/10677f56d8aae95f4c2d7ef4b560f21bc18913eb/lib/google/appengine/ext/blobstore/uploadapp.js
--------------------------------------------------------------------------------
/lib/google/appengine/ext/db.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview A port of the Python google.appengine.ext.db API to Javascript.
3 | *
4 | * http://code.google.com/appengine/docs/python/datastore/queryclass.html#Query
5 | */
6 |
7 | var DATASTORE = require("google/appengine/api/datastore"),
8 | DatastoreRPC = DATASTORE.DatastoreRPC;
9 |
10 | var JDatastore = DATASTORE.Datastore,
11 | JEntityNotFoundException = Packages.com.google.appengine.api.datastore.EntityNotFoundException,
12 | JKey = Packages.com.google.appengine.api.datastore.Key,
13 | JArrayList = java.util.ArrayList;
14 |
15 | var argsArray = Array.prototype.splice,
16 | isArray = Array.isArray;
17 |
18 | mod = require("google/appengine/ext/db/utils");
19 | for (var i in mod) exports[i] = mod[i];
20 |
21 | var mod = require("google/appengine/ext/db/query");
22 | for (var i in mod) exports[i] = mod[i];
23 |
24 | mod = require("google/appengine/ext/db/properties");
25 | for (var i in mod) exports[i] = mod[i];
26 |
27 | mod = require("google/appengine/ext/db/errors");
28 | for (var i in mod) exports[i] = mod[i];
29 |
30 | mod = require("google/appengine/ext/db/model");
31 | for (var i in mod) exports[i] = mod[i];
32 |
33 | mod = require("google/appengine/api/datastore/types");
34 | for (var i in mod) exports[i] = mod[i];
35 |
36 | var Query = exports.Query,
37 | Key = exports.Key,
38 | entityToObject = exports.entityToObject,
39 | objectToEntity = exports.objectToEntity;
40 |
41 | exports.STRONG_CONSISTENCY = DATASTORE.STRONG_CONSISTENCY;
42 | exports.EVENTUAL_CONSISTENCY = DATASTORE.EVENTUAL_CONSISTENCY;
43 |
44 | /**
45 | * Create an rpc for use in configuring datastore calls.
46 | *
47 | * Args:
48 | * deadline: float, deadline for calls in seconds.
49 | * callback: callable, a callback triggered when this rpc completes,
50 | * accepts one argument: the returned rpc.
51 | * read_policy: flag, set to EVENTUAL_CONSISTENCY to enable eventually
52 | * consistent reads
53 | *
54 | * Returns:
55 | * A datastore.DatastoreRPC instance.
56 | */
57 | exports.createRPC = function (args) {
58 | return new DatastoreRPC(args);
59 | }
60 |
61 | /**
62 | * Gets the entity or entities for the given key or keys, of any Model.
63 | */
64 | exports.get = function (keys) {
65 | if (isArray(keys)) {
66 | if (typeof(keys[0]) == "string") {
67 | keys = keys.map(function (k) { new Key(k) });
68 | }
69 | } else {
70 | if (typeof(keys) == "string") {
71 | keys = new Key(keys);
72 | }
73 | }
74 |
75 | if (isArray(keys)) {
76 | var list = new JArrayList(keys.length);
77 | for (var i = 0; i < keys.length; i++) list.add(keys[i].__key__);
78 | var entities = JDatastore.get(list).values();
79 |
80 | var objects = [];
81 | for (var e in Iterator(entities)) {
82 | objects.push(entityToObject(e));
83 | }
84 |
85 | return objects;
86 | } else {
87 | var entity;
88 | try {
89 | entity = JDatastore.get(keys.__key__);
90 | } catch (e) {
91 | if (e.javaException instanceof JEntityNotFoundException)
92 | return null;
93 | else
94 | throw e;
95 | }
96 | return entityToObject(entity);
97 | }
98 | }
99 |
100 | /**
101 | * Puts one or more model instances into the datastore.
102 | */
103 | // FIXME: wrap returned Key!
104 | exports.put = function (objects) {
105 | if (isArray(objects)) {
106 | if (objects.length > 0) {
107 | var list = new JArrayList(objects.length);
108 | for (var i = 0; i < objects.length; i++) list.add(objects[i]._populateEntity());
109 | return JDatastore.put(list);
110 | } else {
111 | return [];
112 | }
113 | } else {
114 | return JDatastore.put(objects._populateEntity());
115 | }
116 | }
117 |
118 | /**
119 | * Deletes one or more model instances from the datastore.
120 | *
121 | * WARNING: 'delete' is a reserved word in JavaScript so model.remove() or
122 | * model.DELETE() are used instead!
123 | */
124 | // TODO: use model, string or Key.
125 | exports.remove = exports.DELETE = function (keys) {
126 | if (isArray(keys)) {
127 | if (keys.length > 0) {
128 | var list = new JArrayList(keys.length);
129 | // FIXME: implement with UTILS.datastoreKey();
130 | // for (var i = 0; i < keys.length; i++) list.add(keys[i].__key__);
131 | for (var i = 0; i < keys.length; i++) list.add(keys[i].datastoreKey());
132 | JDatastore["delete"](list);
133 | }
134 | } else {
135 | JDatastore["delete"](keys.__key__);
136 | }
137 | }
138 |
139 | /**
140 | * Runs a function containing datastore updates in a single transaction. If any
141 | * code raises an exception during the transaction, all datastore updates made
142 | * in the transaction are rolled back.
143 | *
144 | * http://code.google.com/appengine/docs/python/datastore/transactions.html
145 | */
146 | exports.runInTransaction = function () {
147 | var result;
148 | var args = argsArray.call(arguments, 0)
149 | var func = args.shift();
150 |
151 | var tx = JDatastore.beginTransaction ();
152 | try {
153 | result = func.apply(null, args);
154 | } catch (e) {
155 | tx.rollback();
156 | throw e;
157 | }
158 | tx.commit();
159 |
160 | if (undefined != result) return result;
161 | }
162 |
163 | /**
164 | * Create a datastore key.
165 | */
166 | exports.key = exports.Key.create;
167 |
168 |
--------------------------------------------------------------------------------
/lib/google/appengine/ext/db/errors.js:
--------------------------------------------------------------------------------
1 | var defineError = require("google/appengine/utils").defineError;
2 |
3 | exports.KindError = defineError("KindError");
4 | exports.BadValueError = defineError("BadValueError");
5 | exports.NotSavedError = defineError("NotSavedError");
6 |
--------------------------------------------------------------------------------
/lib/google/appengine/ext/db/forms.js:
--------------------------------------------------------------------------------
1 | var db = require("google/appengine/ext/db");
2 |
3 | /**
4 | * Encapsulates a form. Based on Django forms.
5 | *
6 | * http://docs.djangoproject.com/en/dev/topics/forms/
7 | *
8 | * @constructor
9 | */
10 | var Form = exports.Form = function () {
11 | }
12 |
13 | /**
14 | * A form tied to a Datastore model.
15 | *
16 | * Usage example:
17 | *
18 | * var db = require("google/appengine/ext/db"),
19 | * forms = require("google/appengine/ext/db/forms");
20 | *
21 | * // First, define a model class
22 | * var MyModel = db.Model("MyModel", {
23 | * foo: new db.StringProperty(),
24 | * bar: new db.IntegerProperty({required: true, defaultValue: 42})
25 | * });
26 | *
27 | * // Now define a form class
28 | * var MyForm = ModelForm(MyModel);
29 | *
30 | * You can now instantiate MyForm without arguments to create an
31 | * unbound form, or with data from a POST request to create a bound
32 | * form. You can also pass a model instance with the instance=...
33 | * keyword argument to create an unbound (!) form whose initial values
34 | * are taken from the instance. For bound forms, use the put() method
35 | * to return a model instance.
36 | *
37 | * Like Django's own corresponding ModelForm class, the nested Meta
38 | * class can have two other attributes:
39 | *
40 | * fields: if present and non-empty, a list of field names to be
41 | * included in the form; properties not listed here are
42 | * excluded from the form
43 | *
44 | * exclude: if present and non-empty, a list of field names to be
45 | * excluded from the form
46 | *
47 | * If exclude and fields are both non-empty, names occurring in both
48 | * are excluded (i.e. exclude wins). By default all property in the
49 | * model have a corresponding form field defined.
50 | *
51 | * It is also possible to define form fields explicitly. This gives
52 | * more control over the widget used, constraints, initial value, and
53 | * so on. Such form fields are not affected by the nested Meta class's
54 | * fields and exclude attributes.
55 | *
56 | * If you define a form field named 'keyName' it will be treated
57 | * specially and will be used as the value for the keyName parameter
58 | * to the Model constructor. This allows you to create instances with
59 | * named keys. The 'keyName' field will be ignored when updating an
60 | * instance (although it will still be shown on the form).
61 | *
62 | * http://docs.djangoproject.com/en/dev/topics/forms/modelforms/
63 | *
64 | * @constructor
65 | */
66 | var ModelForm = exports.ModelForm = function (kind) {
67 | var KindForm = function (data, options) {
68 | print(JSON.stringify(data));
69 | options = options || {};
70 |
71 | if (options.instance) {
72 | this.instance = options.instance;
73 | } else {
74 | this.instance = new kind();
75 | }
76 |
77 | formToObject(data, this.instance);
78 |
79 | this.validate();
80 | }
81 |
82 | KindForm.prototype = Object.create(ModelForm.prototype);
83 | KindForm.constructor = KindForm;
84 | /*
85 | accessor helpers:
86 | for (var pname in kind.properties()) {
87 | var p = kind.properties()[pname];
88 | KindForm.prototype[pname] = function () {
89 | return p.getFormField(this.instance);
90 | }
91 | }
92 | */
93 | return KindForm;
94 | }
95 |
96 | // ModelForm extends from Form.
97 | ModelForm.prototype = Object.create(Form.prototype);
98 | ModelForm.constructor = ModelForm;
99 |
100 | /**
101 | * Check if the form contains errors.
102 | *
103 | * @return {boolean} true if the form contains errors, false otherwise.
104 | */
105 | ModelForm.prototype.isValid = function () {
106 | for (var e in this.errors) return false;
107 | return true;
108 | }
109 |
110 | ModelForm.prototype.asList = ModelForm.prototype.asUl = function () {
111 | var obj = this.instance,
112 | properties = this.instance.constructor.properties();
113 |
114 | var html = [];
115 |
116 | for (var i in properties) {
117 | var p = properties[i];
118 | html.push('
' + p.getFormField(obj) + '
');
119 | }
120 |
121 | return html.join("");
122 | }
123 |
124 | ModelForm.prototype.validate = function () {
125 | var obj = this.instance,
126 | properties = this.instance.constructor.properties();
127 |
128 | this.errors = {};
129 |
130 | for (var i in properties) {
131 | var p = properties[i];
132 | try {
133 | p.validate(obj[p.name]);
134 | } catch(e) {
135 | this.errors[p.name] = e.message;
136 | }
137 | }
138 | /*
139 | return this.errors;
140 | */
141 | }
142 |
143 | /**
144 | * Save this form's cleaned data into a model instance.
145 | *
146 | * Args:
147 | * commit: optional bool, default True; if true, the model instance
148 | * is also saved to the datastore.
149 | *
150 | * Returns:
151 | * A model instance. If a model instance was already associated
152 | * with this form instance (either passed to the constructor with
153 | * instance=... or by a previous save() call), that same instance
154 | * is updated and returned; if no instance was associated yet, one
155 | * is created by this call.
156 | *
157 | * Raises:
158 | * ValueError if the data couldn't be validated.
159 | */
160 | ModelForm.prototype.put = ModelForm.prototype.save = function () {
161 | /*
162 | this.validate();
163 | for (var i in this.errors) {
164 | throw this.errors;
165 | }
166 | */
167 | this.instance.put();
168 | return this.instance;
169 | }
170 |
171 | /**
172 | */
173 | ModelForm.prototype.field = function (name) {
174 | return this.instance.constructor.properties()[name].getFormField(this.instance);
175 | }
176 |
177 | // Update properties with form related methods.
178 |
179 | /**
180 | * Return a form field appropriate for this property.
181 | */
182 | db.Property.prototype.getFormField = function (obj) {
183 | return 'no field';
184 | }
185 |
186 | /**
187 | * Extract the property value from the instance for use in a form.
188 | */
189 | db.Property.prototype.getValueForForm = function (obj) {
190 | return (obj[this.name] || "").toString();
191 | }
192 |
193 | /**
194 | * Convert a form value to a property value.
195 | */
196 | db.Property.prototype.makeValueFromForm = function (value) {
197 | return value;
198 | }
199 |
200 | db.IntegerProperty.prototype.getFormField = function (obj) {
201 | return '';
202 | }
203 |
204 | db.IntegerProperty.prototype.getValueForForm = function (obj) {
205 | return obj[this.name];
206 | }
207 |
208 | db.IntegerProperty.prototype.makeValueFromForm = function (value) {
209 | return parseInt(value, 10);
210 | }
211 |
212 | db.FloatProperty.prototype.makeValueFromForm = function (value) {
213 | return parseFloat(value);
214 | }
215 |
216 | db.StringProperty.prototype.getFormField = function (obj) {
217 | return '';
218 | }
219 |
220 | db.StringProperty.prototype.makeValueFromForm = function (value) {
221 | return value;
222 | }
223 |
224 | db.TextProperty.prototype.getFormField = function (obj) {
225 | return '';
226 | }
227 |
228 | db.TextProperty.prototype.makeValueFromForm = function (value) {
229 | return value;
230 | }
231 |
232 | db.DateTimeProperty.prototype.makeValueFromForm = function (value) {
233 | return new Date(value);
234 | }
235 |
236 | db.ReferenceProperty.prototype.makeValueFromForm = function (value) {
237 | return new db.Key(value);
238 | }
239 |
240 | db.EmailProperty.prototype.getFormField = function (obj) {
241 | return '';
242 | }
243 |
244 | // -----------------------------------------------------------------------------
245 | // Old stuff, still used, cleanup needed.
246 |
247 | /**
248 | *
249 | */
250 | exports.objectToForm = function (obj, prefix) {
251 | var properties = obj.constructor.properties();
252 |
253 | var form = "";
254 |
255 | for (var name in properties) {
256 | var property = properties[name];
257 | if (property.editable == false) continue;
258 |
259 | var value = obj[name];
260 |
261 | var label = property.name;
262 | var field = prefix ? prefix + "[" + property.name + "]" : property.name;
263 |
264 | switch (property.constructor) {
265 | case db.StringProperty:
266 | form += '
";
270 | break;
271 |
272 | case db.IntegerProperty:
273 | form += '
";
277 | break;
278 |
279 | case db.FloatProperty:
280 | form += '
";
284 | break;
285 |
286 | case db.DateProperty:
287 | case db.DateTimeProperty:
288 | form += '
";
294 | break;
295 |
296 | case db.TextProperty:
297 | form += '
";
301 | break;
302 |
303 | case db.ReferenceProperty:
304 | form += '
';
305 | if (property.referenceClass) {
306 | form += '";
315 | } else {
316 | form += '";
320 | }
321 | if (value != undefined) {
322 | form += '  link»';
323 | }
324 | break;
325 | }
326 | }
327 |
328 | return form;
329 | }
330 |
331 | var formToObject = exports.formToObject = function (form, obj) {
332 | var properties = obj.constructor.properties();
333 |
334 | for (var name in properties) {
335 | var property = properties[name];
336 | if (property.editable != false) {
337 | if (form[name]) obj[name] = property.makeValueFromForm(form[name]);
338 | }
339 | }
340 |
341 | return obj;
342 | }
343 |
--------------------------------------------------------------------------------
/lib/google/appengine/ext/db/model.js:
--------------------------------------------------------------------------------
1 | var datastore = require("google/appengine/api/datastore"),
2 | JDatastore = datastore.Datastore,
3 | JEntity = datastore.Entity;
4 |
5 | var objects = require("ringo/utils/objects");
6 |
7 | var db = require("google/appengine/ext/db"),
8 | Query = db.Query,
9 | ListProperty = db.ListProperty,
10 | types = require("google/appengine/api/datastore/types"),
11 | Key = types.Key,
12 | errors = require("google/appengine/ext/db/errors"),
13 | KindError = errors.KindError,
14 | NotSavedError = errors.NotSavedError;
15 |
16 | /**
17 | * Model is the superclass of all object entities in the datastore.
18 | *
19 | * A model instance can have a single parent. Model instances without any
20 | * parent are root entities. It is possible to efficiently query for
21 | * instances by their shared parent. All descendents of a single root
22 | * instance also behave as a transaction group. This means that when you
23 | * work one member of the group within a transaction all descendents of that
24 | * root join the transaction. All operations within a transaction on this
25 | * group are ACID.
26 | *
27 | * @constructor
28 | */
29 | var Model = exports.Model = function (kind, properties) {
30 | return Model.extend(kind, properties);
31 | }
32 |
33 | Model._properties = {};
34 |
35 | Model._loadEntityValues = function (entity) {
36 | var values = {},
37 | properties = this.properties();
38 |
39 | for (var prop in Iterator(entity.getProperties().entrySet())) {
40 | var pname = prop.getKey();
41 | var property = properties[pname];
42 | if (undefined != property) { // THINK: extra safety for changing schemas?
43 | values[pname] = property.makeValueFromDatastore(prop.getValue());
44 | }
45 | }
46 |
47 | return values;
48 | }
49 |
50 | var _initializeProperties = function (modelClass, properties) {
51 | for (var pname in properties) {
52 | var property = properties[pname];
53 | property.name = pname;
54 | if (property.init) {
55 | property.init(modelClass);
56 | } else {
57 | throw new Error("Invalid property class for property '" + pname + "'");
58 | }
59 | }
60 |
61 | return properties;
62 | }
63 |
64 | /**
65 | * Extend the given Model (simulates Python class extensions).
66 | *
67 | * Examples:
68 | * var Article = db.Model.extend("Article", {
69 | * title: new db.StringProperty(),
70 | * summary: new db.TextProperty()
71 | * });
72 | *
73 | * var Story = Article.extend("Story", {
74 | * image: new db.BlobProperty(),
75 | * author: new db.ReferenceProperty(User)
76 | * });
77 | *
78 | * Alternative:
79 | * var Article = db.Model.extend({
80 | * _kind: "Article",
81 | * title: new db.StringProperty(),
82 | * summary: new db.TextProperty()
83 | * });
84 | *
85 | })
86 | *
87 | */
88 | Model.extend = function (kind, properties) {
89 | if (1 == arguments.length) {
90 | properties = kind;
91 | kind = properties._kind;
92 | delete properties._kind;
93 | }
94 |
95 | if (kind == undefined) {
96 | throw Error("The kind is undefined");
97 | }
98 |
99 | var ctor = function (data) {
100 | if (data) {
101 | if (data.key) {
102 | this.__key__ = data.key;
103 | delete data.key;
104 | } else if (data.keyName || data.parent) {
105 | this.__key__ = Key.create({kind: this.constructor.kind(), parent: data.parent, name: data.keyName});
106 | delete data.keyName;
107 | delete data.parent;
108 | }
109 |
110 | for (var pname in data) {
111 | this[pname] = data[pname];
112 | }
113 | }
114 | }
115 |
116 | // ctor.__proto__ = this; // disabled in ringojs, use this alternative:
117 | var self = this;
118 | Object.keys(this).forEach(function (k) { ctor[k] = self[k] });
119 |
120 | ctor.prototype = Object.create(this.prototype);
121 | ctor.prototype.constructor = ctor;
122 |
123 | ctor._kind = kind;
124 | db.kindMap[kind] = ctor;
125 |
126 | ctor._properties = objects.merge(this.properties(), _initializeProperties(ctor, properties));
127 |
128 | return ctor;
129 | }
130 |
131 | Model.kind = function () {
132 | return this._kind;
133 | }
134 |
135 | Model.properties = function () {
136 | return this._properties;
137 | }
138 |
139 | Model.updateProperties = function (newProperties) {
140 | this._properties = objects.merge(this._properties, _initializeProperties(this, newProperties));
141 | }
142 |
143 | Model.fromEntity = function (entity) {
144 | if (this.kind() != String(entity.getKind())) {
145 | throw new KindError("Model " + this.kind() + " cannot handle " + entity.getKind());
146 | }
147 |
148 | var instance = new this(this._loadEntityValues(entity));
149 | instance._entity = entity;
150 |
151 | return instance;
152 | }
153 |
154 | /**
155 | * Gets the model instance (or instances) for the given Key objects. The keys
156 | * must represent entities of the model's kind. If a provided key is not of the
157 | * correct kind, a KindError is raised.
158 | *
159 | * This method is similar to the db.get() function, with additional type checking.
160 | *
161 | * Arguments:
162 | *
163 | * keys = A Key object or a list of Key objects.
164 | * Can also be a string version of a Key object, or list of strings.
165 | */
166 | Model.get = function (keys) {
167 | if (!keys) return;
168 |
169 | var objs = db.get(keys);
170 |
171 | if (!objs) return null;
172 |
173 | var kind = this.kind();
174 | var arr = Array.isArray(objs) ? objs : [objs];
175 | for (var i = 0; i < arr.length; i++) {
176 | if (arr[i].constructor.kind() != kind)
177 | throw new KindError("Instance is of kind '" + arr[i].constructor.kind() + "', expected kind is '" + kind + "'");
178 | }
179 |
180 | return objs;
181 | }
182 |
183 | Model.getById = function (ids, parent) {
184 | var keys;
185 |
186 | if (Array.isArray(ids)) {
187 | throw new Error("Not implemented");
188 | } else {
189 | throw new Error("Not implemented");
190 | }
191 |
192 | return this.get(keys);
193 | }
194 |
195 | Model.getByKeyName = function (names, parent) {
196 | var keys;
197 |
198 | if (Array.isArray(names)) {
199 | var self = this;
200 | keys = names.map(function (n) { return Key.create({parent: parent, kind: self.kind(), name: n}) });
201 | } else {
202 | keys = Key.create({parent: parent, kind: this.kind(), name: names});
203 | }
204 |
205 | return this.get(keys);
206 | }
207 |
208 | /**
209 | * Get or create an entity of the model's kind with the given key name, using a
210 | * single transaction. The transaction ensures that if two users attempt to
211 | * get-or-insert the entity with the given name simultaneously, then both users
212 | * will have a model instance that refers to the entity, regardless of which
213 | * process created it.
214 | *
215 | * - name:
216 | * the name of the instance.
217 | * - data:
218 | * constructor data.
219 | */
220 | Model.getOrInsert = function (keyName, data) {
221 | var obj,
222 | self = this;
223 |
224 | db.runInTransaction (function () {
225 | if (!data) data = {};
226 | obj = self.getByKeyName(keyName, data.parent);
227 | if (!obj) {
228 | data.keyName = keyName;
229 | obj = new self(data);
230 | obj.put();
231 | }
232 | });
233 |
234 | return obj;
235 | }
236 |
237 | Model.all = function () {
238 | return new Query(this);
239 | }
240 |
241 | Model.prototype._toEntity = function (entity) {
242 | var properties = this.constructor.properties();
243 |
244 | for (var pname in properties) {
245 | var property = properties[pname];
246 | var value = property.getValueForDatastore(this);
247 | if (value == undefined) {
248 | entity.removeProperty(pname);
249 | } else {
250 | if (property.indexed) {
251 | entity.setProperty(pname, value);
252 | } else {
253 | entity.setUnindexedProperty(pname, value);
254 | }
255 | }
256 | }
257 | }
258 |
259 | Model.prototype._populateEntity = function () {
260 | var entity;
261 |
262 | if (this.isSaved()) {
263 | entity = this._entity;
264 | } else {
265 | var key = this.__key__ || Key.create({kind: this.constructor.kind()});
266 | entity = new JEntity(key.datastoreKey());
267 | }
268 |
269 | this._toEntity(entity);
270 |
271 | return entity;
272 | }
273 |
274 | Model.prototype._populateInternalEntity = function () {
275 | this._entity = this._populateEntity();
276 | return this._entity;
277 | }
278 |
279 | Model.prototype.key = function () {
280 | if (this.__key__) {
281 | return this.__key__;
282 | } else if (this.isSaved()) {
283 | this.__key__ = Key.fromDatastoreKey(this._entity.getKey());
284 | return this.__key__;
285 | } else {
286 | throw new NotSavedError("Not saved");
287 | }
288 | }
289 |
290 | // FIXME: optimize? API extension, used in db.js
291 | Model.prototype.datastoreKey = function () {
292 | return this.key() ? this.key().__key__ : null;
293 | }
294 |
295 | Model.prototype.parent = function () {
296 | if (!this._parent) {
297 | var parentKey = this.parentKey();
298 | if (parentKey != undefined) {
299 | this._parent = db.get(parentKey);
300 | }
301 | }
302 |
303 | return this._parent;
304 | }
305 |
306 | Model.prototype.parentKey = function () {
307 | if (this._entity) {
308 | var jparentKey = this._entity.getParent();
309 | if (jparentKey) {
310 | return Key.fromDatastoreKey(this._entity.getParent());
311 | } else {
312 | return null;
313 | }
314 | } else if (this.__key__) {
315 | return this.__key__.parent();
316 | }
317 | }
318 |
319 | Model.prototype.put = function () {
320 | this._populateInternalEntity();
321 | var jkey = JDatastore.put(this._entity);
322 | return Key.fromDatastoreKey(jkey);
323 | }
324 |
325 | /**
326 | * Deletes this entity from the datastore.
327 | *
328 | * WARNING: 'delete' is a reserved word in JavaScript so model.remove() or
329 | * model.DELETE() are used instead!
330 | *
331 | * Throws:
332 | * TransactionFailedError if the data could not be committed.
333 | *
334 | */
335 | Model.prototype.remove = Model.prototype.DELETE = function () {
336 | db.remove(this.key());
337 | this._entity = null;
338 | }
339 |
340 | /**
341 | * Determine if entity is persisted in the datastore.
342 | *
343 | * New instances of Model do not start out saved in the data. Objects which
344 | * are saved to or loaded from the Datastore will have a True saved state.
345 | */
346 | Model.prototype.isSaved = function () {
347 | return this._entity;
348 | }
349 |
350 | Model.prototype.hasKey = function () {
351 | return this.isSaved() || this._key;
352 | }
353 |
354 | /**
355 | * Convert to a data object. First step to JSON conversion.
356 | */
357 | Model.prototype.toData = function () {
358 | var data = {
359 | key: this.key().toString()
360 | }
361 |
362 | var properties = this.constructor.properties();
363 |
364 | for (var i in properties) {
365 | var prop = properties[i];
366 |
367 | data[prop.name] = this[prop.name];
368 | }
369 |
370 | return data;
371 | }
372 |
373 | /**
374 | * Serialize model data to a JSON string.
375 | */
376 | Model.prototype.toJSON = function () {
377 | return JSON.stringify(this.toData());
378 | }
379 |
--------------------------------------------------------------------------------
/lib/google/appengine/ext/db/properties.js:
--------------------------------------------------------------------------------
1 | var JText = Packages.com.google.appengine.api.datastore.Text,
2 | JBlob = Packages.com.google.appengine.api.datastore.Blob,
3 | JGeoPt = Packages.com.google.appengine.api.datastore.GeoPt;
4 |
5 | var jcontext = Packages.org.mozilla.javascript.Context.getCurrentContext(),
6 | JArrays = Packages.java.util.Arrays,
7 | JArrayList = java.util.ArrayList;
8 |
9 | var ByteString = require("binary").ByteString;
10 |
11 | var BadValueError = require("google/appengine/ext/db/errors").BadValueError;
12 |
13 | var TYPES = require("google/appengine/api/datastore/types"),
14 | Key = TYPES.Key,
15 | GeoPt = TYPES.GeoPt;
16 |
17 | var utils = require("google/appengine/utils"),
18 | users = require("google/appengine/api/users");
19 |
20 | // property constructor helper.
21 | var _makeProperty = function (ctor, options) {
22 | var p = Object.create(ctor.prototype);
23 | for (var i in options) p[i] = options[i];
24 | if (p.required == undefined) p.required = false;
25 | if (p.indexed == undefined) p.indexed = true;
26 | return p;
27 | }
28 |
29 | /**
30 | * The Property class is the superclass of property definitions for data models.
31 | * A Property class defines the type of a property's value, how values are
32 | * validated, and how values are stored in the datastore.
33 | *
34 | * Arguments:
35 | * - verboseName
36 | * - name
37 | * - defaultValue
38 | * - required
39 | * - validator
40 | * - choices
41 | * - indexed
42 | *
43 | * @constructor
44 | */
45 | var Property = exports.Property = function (options) {
46 | for (var i in options) this[i] = options[i];
47 | if (this.required == undefined) this.required = false;
48 | if (this.indexed == undefined) this.indexed = true;
49 | }
50 |
51 | Property.prototype.validate = function (value) {
52 | if (this.empty(value)) {
53 | if (this.required) throw new BadValueError(this.verbose() + " is required");
54 | } else {
55 | if (this.choices) {
56 | var match = false;
57 | for (var i = 0; i < this.choices.length; i++)
58 | if (this.choices[i] == value) match = true;
59 | if (!match)
60 | throw new BadValueError(this.verbose() + " is " + value + "; must be one of " + this.choices);
61 | }
62 | }
63 |
64 | if (this.validator) this.validator(value); // hmm, maybe return here?
65 |
66 | return value;
67 | }
68 |
69 | Property.prototype.empty = function (value) {
70 | return value == undefined || value == null;
71 | }
72 |
73 | Property.prototype._attrName = function () {
74 | return this.name;
75 | }
76 |
77 | /**
78 | * @constructor
79 | */
80 | var StringProperty = exports.StringProperty = function (options) {
81 | return _makeProperty(StringProperty, options);
82 | }
83 |
84 | utils.extend(StringProperty, Property);
85 |
86 | StringProperty.prototype.init = function (constructor) {
87 | }
88 |
89 | /** @override */
90 | StringProperty.prototype.getValueForDatastore = function (obj) {
91 | var val = obj[this.name];
92 |
93 | if (val == undefined) {
94 | if (this.defaultValue != undefined) {
95 | obj[this.name] = this.defaultValue;
96 | return this.defaultValue;
97 | } else {
98 | return null;
99 | }
100 | } else {
101 | return val;
102 | }
103 | }
104 |
105 | /** @override */
106 | StringProperty.prototype.makeValueFromDatastore = function (value) {
107 | return value;
108 | }
109 |
110 | StringProperty.prototype.empty = function (value) {
111 | return value == undefined || value == null || (value.length < 1);
112 | }
113 |
114 | /**
115 | * @constructor
116 | */
117 | var BooleanProperty = exports.BooleanProperty = function (options) {
118 | return _makeProperty(BooleanProperty, options);
119 | }
120 |
121 | utils.extend(BooleanProperty, Property);
122 |
123 | BooleanProperty.prototype.init = function (constructor) {
124 | }
125 |
126 | /** @override */
127 | BooleanProperty.prototype.getValueForDatastore = function (obj) {
128 | var val = obj[this.name];
129 |
130 | if (val == undefined) {
131 | if (this.defaultValue != undefined) {
132 | obj[this.name] = this.defaultValue;
133 | return this.defaultValue;
134 | } else {
135 | return null;
136 | }
137 | } else {
138 | return val;
139 | }
140 | }
141 |
142 | /** @override */
143 | BooleanProperty.prototype.makeValueFromDatastore = function (value) {
144 | // return value.valueOf();
145 | return value;
146 | }
147 |
148 | //var JLong = java.lang.Long;
149 |
150 | /**
151 | * @constructor
152 | */
153 | var IntegerProperty = exports.IntegerProperty = function (options) {
154 | return _makeProperty(IntegerProperty, options);
155 | }
156 |
157 | utils.extend(IntegerProperty, Property);
158 |
159 | IntegerProperty.prototype.init = function (constructor) {
160 | }
161 |
162 | /** @override */
163 | IntegerProperty.prototype.getValueForDatastore = function (obj) {
164 | var val = obj[this.name];
165 |
166 | if (val == undefined) {
167 | if (this.defaultValue != undefined) {
168 | obj[this.name] = this.defaultValue;
169 | return this.defaultValue;
170 | } else {
171 | return null;
172 | }
173 | } else {
174 | // return new JInteger(val); !!! < 2^31
175 | // return new JLong(val);
176 | return val; // FIXME: generates floats?
177 | }
178 | }
179 |
180 | /** @override */
181 | IntegerProperty.prototype.makeValueFromDatastore = function (value) {
182 | // return Number(value);
183 | return value;
184 | }
185 |
186 | /**
187 | * @constructor
188 | */
189 | var FloatProperty = exports.FloatProperty = function (options) {
190 | return _makeProperty(FloatProperty, options);
191 | }
192 |
193 | utils.extend(FloatProperty, Property);
194 |
195 | FloatProperty.prototype.init = function (constructor) {
196 | }
197 |
198 | /** @override */
199 | FloatProperty.prototype.getValueForDatastore = function (obj) {
200 | var val = obj[this.name];
201 |
202 | if (val == undefined) {
203 | if (this.defaultValue != undefined) {
204 | obj[this.name] = this.defaultValue;
205 | return this.defaultValue;
206 | } else {
207 | return null;
208 | }
209 | } else {
210 | return val;
211 | }
212 | }
213 |
214 | // THINK: check for null/undefined? Number(null) = 0, Number(undefined) = NaN, hmm...
215 | // FloatProperty.prototype.makeValueFromDatastore = Number;
216 | FloatProperty.prototype.makeValueFromDatastore = function (value) {
217 | // return Number(value);
218 | return value;
219 | }
220 |
221 | /**
222 | * @constructor
223 | */
224 | var DateProperty = exports.DateProperty = function (options) {
225 | return _makeProperty(DateProperty, options);
226 | }
227 |
228 | utils.extend(DateProperty, Property);
229 |
230 | var JDate = java.util.Date;
231 |
232 | DateProperty.prototype.init = function (constructor) {
233 | }
234 |
235 | DateProperty.prototype.getValueForDatastore = function (obj) {
236 | if (this.autoNow) {
237 | obj[this.name] = new Date();
238 | } else if (this.autoNowAdd && (!obj.isSaved())) {
239 | obj[this.name] = new Date();
240 | } else if ((this.defaultValue != undefined) && (obj[this.name] == undefined)) {
241 | obj[this.name] = this.defaultValue;
242 | }
243 |
244 | var val = obj[this.name];
245 |
246 | if (val == undefined) {
247 | return null;
248 | } else {
249 | return JDate(val.getTime());
250 | }
251 | }
252 |
253 | DateProperty.prototype.makeValueFromDatastore = function (value) {
254 | if (value) {
255 | return new Date(value.getTime());
256 | } else {
257 | return null;
258 | }
259 | }
260 |
261 | /**
262 | * @constructor
263 | */
264 | var DateTimeProperty = exports.DateTimeProperty = DateProperty;
265 | DateTimeProperty.constructor = DateTimeProperty;
266 |
267 | /**
268 | * @constructor
269 | */
270 | var TimeProperty = exports.TimeProperty = DateProperty;
271 | TimeProperty.constructor = TimeProperty;
272 |
273 | /**
274 | * A list of values of the type given as item_type.
275 | * In a query, comparing a list property to a value performs the test against
276 | * the list members: list_property = value tests if the value appears anywhere
277 | * in the list, list_property < value tests if any of the members of the list
278 | * are less than the given value, and so forth.
279 | * A query cannot compare two list values. There is no way to test two lists for
280 | * equality without testing each element for membership separately.
281 | *
282 | * @constructor
283 | */
284 | // FIXME: pass type/ctor of elements.
285 | var ListProperty = exports.ListProperty = function (options) {
286 | return _makeProperty(ListProperty, options);
287 | }
288 |
289 | utils.extend(ListProperty, Property);
290 |
291 | ListProperty.prototype.init = function (constructor) {
292 | }
293 |
294 | ListProperty.prototype.getFilterValue = function (val) {
295 | return val;
296 | }
297 |
298 | ListProperty.prototype.getValueForDatastore = function (obj) {
299 | // TODO: defaultValue
300 | var items = obj[this.name] || this.defaultValue || [];
301 | var list = new JArrayList(items.length);
302 | for (var i = 0; i < items.length; i++) list.add(items[i]);
303 |
304 | return list;
305 | }
306 |
307 | ListProperty.prototype.makeValueFromDatastore = function (value) {
308 | return value ? jcontext.newArray(global, value.toArray()) : null;
309 | }
310 |
311 | /**
312 | * @constructor
313 | */
314 | var StringListProperty = exports.StringListProperty = function (options) {
315 | return _makeProperty(StringListProperty, options);
316 | }
317 |
318 | utils.extend(StringListProperty, ListProperty);
319 |
320 | /**
321 | * A reference to another model instance. For example, a reference may indicate
322 | * a many-to-one relationship between the model with the property and the model
323 | * referenced by the property.
324 | *
325 | * Extra options:
326 | * - referenceClass
327 | * - collectionName
328 | *
329 | * Examples:
330 | * category: new DB.ReferenceProperty(Category);
331 | * author: new DB.ReferenceProperty(User, {collectionName: "articles"});
332 | *
333 | * @constructor
334 | */
335 | var ReferenceProperty = exports.ReferenceProperty = function (class_or_options, options) {
336 | if (options) {
337 | if (class_or_options && class_or_options._kind && class_or_options._properties) {
338 | options.referenceClass = class_or_options;
339 | } else {
340 | throw Error("The first argument must be a Kind");
341 | }
342 | return _makeProperty(ReferenceProperty, options);
343 | } else {
344 | return _makeProperty(ReferenceProperty, class_or_options);
345 | }
346 | }
347 |
348 | utils.extend(ReferenceProperty, Property);
349 |
350 | var capFirst = function (str) {
351 | return str.substring(0, 1).toUpperCase() + str.substring(1);
352 | }
353 |
354 | ReferenceProperty.prototype.init = function (constructor) {
355 | var idName = "_" + this.name,
356 | resolvedName = "_RESOLVED" + idName,
357 | kind = this.referenceClass;
358 |
359 | if (kind) {
360 | Object.defineProperty(constructor.prototype, this.name, {
361 | get: function () {
362 | if (!this[idName]) return undefined;
363 |
364 | if (this[resolvedName] == undefined) {
365 | var value = kind.get(this[idName]);
366 | if (!value) throw new Error("ReferenceProperty failed to be resolved");
367 | this[resolvedName] = value;
368 | }
369 |
370 | return this[resolvedName];
371 | },
372 | set: function (value) { // val = key or model
373 | if (value) {
374 | if (value.constructor == Key) {
375 | this[idName] = value;
376 | this[resolvedName] = undefined;
377 | } else {
378 | this[idName] = value.key();
379 | this[resolvedName] = value;
380 | }
381 | } else {
382 | this[idName] = undefined;
383 | this[resolvedName] = undefined;
384 | }
385 | },
386 | enumerable: true
387 | });
388 |
389 | this.collectionName = this.collectionName || constructor.kind().toLowerCase() + "Set";
390 | var name = this.name;
391 | Object.defineProperty(this.referenceClass.prototype, this.collectionName, {
392 | get: function () {
393 | return constructor.all().filter(name + " =", this);
394 | },
395 | configurable: true // FIXME: ???
396 | });
397 | } else {
398 | // FIXME: think what to do!
399 | Object.defineProperty(constructor.prototype, this.name, {
400 | get: function () {
401 | if (!this[idName]) return undefined;
402 |
403 | if (this[resolvedName] == undefined) {
404 | var value = require("google/appengine/ext/db").get(this[idName]);
405 | if (!value) throw new Error("ReferenceProperty failed to be resolved");
406 | this[resolvedName] = value;
407 | }
408 |
409 | return this[resolvedName];
410 | },
411 | set: function (value) { // val = key or model
412 | if (value) {
413 | if (value.constructor == Key) {
414 | this[idName] = value;
415 | this[resolvedName] = undefined;
416 | } else {
417 | this[idName] = value.key();
418 | this[resolvedName] = value;
419 | }
420 | } else {
421 | this[idName] = undefined;
422 | this[resolvedName] = undefined;
423 | }
424 | },
425 | enumerable: true
426 | });
427 | }
428 | }
429 |
430 | // TODO: enforce referenceClass !!!
431 | // TODO: defaultValue
432 | ReferenceProperty.prototype.getValueForDatastore = function (obj) {
433 | var value = obj[this.name];
434 | return value ? value.datastoreKey() : null;
435 | }
436 |
437 | ReferenceProperty.prototype.makeValueFromDatastore = function (value) {
438 | return Key.fromDatastoreKey(value);
439 | }
440 |
441 | /**
442 | * ReferenceProperty Alias.
443 | * @constructor
444 | */
445 | exports.Reference = ReferenceProperty;
446 |
447 | /**
448 | * A user with a Google account.
449 | *
450 | * If autoCurrentUser is true, the property value is set to the currently
451 | * signed-in user whenever the model instance is stored in the datastore,
452 | * overwriting the property's previous value. This is useful for tracking which
453 | * user modifies a model instance.
454 | *
455 | * If autoCurrentUserAdd is true, the property value is set to the currently
456 | * signed-in user the first time the model instance is stored in the datastore,
457 | * unless the property has already been assigned a value. This is useful for
458 | * tracking which user creates a model instance, which may not be the same
459 | * user that modifies it later.
460 | *
461 | * UserProperty does not accept a default value. Default values are set when
462 | * the model class is first imported, and with import caching may not be the
463 | * currently signed-in user.
464 | *
465 | * @constructor
466 | */
467 | var UserProperty = exports.UserProperty = function (options) {
468 | return _makeProperty(UserProperty, options);
469 | }
470 |
471 | utils.extend(UserProperty, Property);
472 |
473 | UserProperty.prototype.init = function (constructor) {
474 | }
475 |
476 | UserProperty.prototype.getValueForDatastore = function (obj) {
477 | if ((this.autoCurrentUser) || (this.autoCurrentUserAdd && (!obj.isSaved()))) {
478 | var juser = users.UserService.getCurrentUser();
479 | obj[this.name] = users.User.fromJavaUser(juser);
480 | return juser;
481 | }
482 |
483 | var val = obj[this.name];
484 |
485 | if (val == undefined) {
486 | return null;
487 | } else {
488 | return val.toJavaUser();
489 | }
490 | }
491 |
492 | UserProperty.prototype.makeValueFromDatastore = function (value) {
493 | return users.User.fromJavaUser(value);
494 | }
495 |
496 | /**
497 | * A binary data property.
498 | * Blob data is a byte string. For text data, which may involve encoding,
499 | * use TextProperty.
500 | *
501 | * @constructor
502 | */
503 | var BlobProperty = exports.BlobProperty = function (options) {
504 | var p = _makeProperty(BlobProperty, options);
505 | p.indexed = false;
506 | return p;
507 | }
508 |
509 | utils.extend(BlobProperty, Property);
510 |
511 | BlobProperty.prototype.init = function (constructor) {
512 | }
513 |
514 | BlobProperty.prototype.getValueForDatastore = function (obj) {
515 | // return new JBlob(obj[this.name]._bytes);
516 | return new JBlob(obj[this.name]);
517 | }
518 |
519 | /*
520 | BlobProperty.prototype.makeValueFromDatastore = function (value) {
521 | var b = new ByteString();
522 | b._bytes = value.getBytes();
523 | b._offset = 0;
524 | b._length = Number(b._bytes.length);
525 | return b;
526 | }
527 | */
528 | BlobProperty.prototype.makeValueFromDatastore = ByteString.wrap;
529 |
530 | /**
531 | * @constructor
532 | */
533 | var TextProperty = exports.TextProperty = function (options) {
534 | var p = _makeProperty(TextProperty, options);
535 | p.indexed = false;
536 | return p;
537 | }
538 |
539 | utils.extend(TextProperty, Property);
540 |
541 | TextProperty.prototype.init = function (constructor) {
542 | }
543 |
544 | TextProperty.prototype.getValueForDatastore = function (obj) {
545 | var val = obj[this.name];
546 |
547 | if (val == undefined) {
548 | if (this.defaultValue != undefined) {
549 | obj[this.name] = this.defaultValue;
550 | return this.defaultValue;
551 | } else {
552 | return null;
553 | }
554 | } else {
555 | return JText(val);
556 | }
557 | }
558 |
559 | TextProperty.prototype.makeValueFromDatastore = function (value) {
560 | // return String(value.getValue());
561 | return value.getValue();
562 | }
563 |
564 | TextProperty.prototype.empty = function (value) {
565 | return value == undefined || value == null || (value.length < 1);
566 | }
567 |
568 | /**
569 | * A tag, ie a descriptive word or phrase. Entities may be tagged by users,
570 | * and later returned by a queries for that tag. Tags can also be used for
571 | * ranking results (frequency), photo captions, clustering, activity, etc.
572 | *
573 | * @constructor
574 | */
575 | var CategoryProperty = exports.CategoryProperty = function (options) {
576 | return _makeProperty(CategoryProperty, options);
577 | }
578 |
579 | utils.extend(CategoryProperty, StringProperty);
580 |
581 | /**
582 | * A fully qualified URL. Usually http: scheme, but may also be file:, ftp:,
583 | * news:, among others.
584 | *
585 | * @constructor
586 | */
587 | var LinkProperty = exports.LinkProperty = function (options) {
588 | return _makeProperty(LinkProperty, options);
589 | }
590 |
591 | utils.extend(LinkProperty, StringProperty);
592 |
593 | /**
594 | * @constructor
595 | */
596 | var EmailProperty = exports.EmailProperty = function (options) {
597 | return _makeProperty(EmailProperty, options);
598 | }
599 |
600 | utils.extend(EmailProperty, StringProperty);
601 |
602 | /**
603 | * @constructor
604 | */
605 | var GeoPtProperty = exports.GeoPtProperty = function (options) {
606 | return _makeProperty(GeoPtProperty, options);
607 | }
608 |
609 | utils.extend(GeoPtProperty, Property);
610 |
611 | GeoPtProperty.prototype.init = function (constructor) {
612 | }
613 |
614 | GeoPtProperty.prototype.getValueForDatastore = function (obj) {
615 | var pt = obj[this.name] || this.defaultValue;
616 | return new JGeoPt(pt.lat, pt.lon);
617 | }
618 |
619 | GeoPtProperty.prototype.makeValueFromDatastore = function (value) {
620 | return new GeoPt(value.getLatitude(), value.getLongitude());
621 | }
622 |
623 | /**
624 | * @constructor
625 | */
626 | var PhoneNumberProperty = exports.PhoneNumberProperty = StringProperty;
627 | PhoneNumberProperty.constructor = PhoneNumberProperty;
628 |
629 | /**
630 | * @constructor
631 | */
632 | var PostalAddressProperty = exports.PostalAddressProperty = StringProperty;
633 | PostalAddressProperty.constructor = PostalAddressProperty;
634 |
635 | /**
636 | * @constructor
637 | */
638 | var RatingProperty = exports.RatingProperty = IntegerProperty;
639 | RatingProperty.constructor = RatingProperty;
640 |
641 |
--------------------------------------------------------------------------------
/lib/google/appengine/ext/db/query.js:
--------------------------------------------------------------------------------
1 | var datastore = require("google/appengine/api/datastore"),
2 | JDatastore = datastore.Datastore,
3 | JQuery = datastore.Query,
4 | JFetchOptions = Packages.com.google.appengine.api.datastore.FetchOptions,
5 | JFetchOptionsBuilder = Packages.com.google.appengine.api.datastore.FetchOptions.Builder,
6 | JCursor = Packages.com.google.appengine.api.datastore.Cursor,
7 | JString = java.lang.String;
8 |
9 | var entityToObject = require("google/appengine/ext/db/utils").entityToObject;
10 |
11 | var Key = require("google/appengine/api/datastore/types").Key;
12 |
13 | var DESCENDING = JQuery.SortDirection.DESCENDING;
14 |
15 | /**
16 | * The Query class is a datastore query interface that uses objects and methods
17 | * to prepare queries.
18 | * @constructor
19 | * @param {} modelClass The class of the Model (or Expando) that represents the datastore entity kind for the query.
20 | * @param {boolean} keysOnly Whether the query should return full entities or only keys.
21 | * @param {boolean} compile Whether the query should also return a compiled query.
22 | * @param {} cursor A compiled query from which to resume.
23 | * @param {string} namespace The namespace to query.
24 | */
25 | var Query = exports.Query = function (modelClass, keysOnly, compile, cursor, namespace) {
26 | this.properties = modelClass.properties();
27 | this.query = new JQuery(modelClass.kind()); // gmosx: not your father's jQuery ;-)
28 | if (keysOnly) this.query.setKeysOnly();
29 | // TODO: handle compiled, namespace.
30 | }
31 |
32 | var FILTER_OPERATORS = {
33 | "=": JQuery.FilterOperator.EQUAL,
34 | ">": JQuery.FilterOperator.GREATER_THAN,
35 | ">=": JQuery.FilterOperator.GREATER_THAN_OR_EQUAL,
36 | "<": JQuery.FilterOperator.LESS_THAN,
37 | "<=": JQuery.FilterOperator.LESS_THAN_OR_EQUAL
38 | }
39 |
40 | /**
41 | * Adds a property condition filter to the query. Only entities with properties
42 | * that meet all of the conditions will be returned by the query.
43 | */
44 | Query.prototype.filter = function (property_op, value) {
45 | var parts = property_op.split(" ");
46 |
47 | if (parts[0] == "__key__") {
48 | this.query.addFilter(parts[0], FILTER_OPERATORS[parts[1]], value.datastoreKey());
49 | } else {
50 | var property = this.properties[parts[0]];
51 |
52 | var filterValue;
53 | if (property.getFilterValue) { // FIXME: this is a hack, a better solution is needed.
54 | filterValue = property.getFilterValue(value);
55 | } else {
56 | var obj = {};
57 | obj[property.name] = value;
58 | filterValue = property.getValueForDatastore(obj)
59 | }
60 |
61 | this.query.addFilter(parts[0], FILTER_OPERATORS[parts[1]], filterValue);
62 | }
63 |
64 | return this;
65 | }
66 |
67 | /**
68 | * Adds an ordering for the results. Results are ordered starting with the first
69 | * order added.
70 | * @arguments:
71 | * property
72 | * A string, the name of the property to order. To specify that the order
73 | * ought to be in descending order, precede the name with a hyphen (-). Without
74 | * a hyphen, the order is ascending.
75 | */
76 | Query.prototype.order = function (property) {
77 | if (property.substr(0,1) == "-")
78 | this.query.addSort(property.slice(1), DESCENDING);
79 | else
80 | this.query.addSort(property);
81 | return this;
82 | }
83 |
84 | Query.prototype.ancestor = function (ancestor) {
85 | this.query.setAncestor(ancestor.datastoreKey());
86 | return this;
87 | }
88 |
89 | Query.prototype.keysOnly = function () {
90 | this.query.setKeysOnly();
91 | return this;
92 | }
93 |
94 | // FIXME: Deprecate, not available in the Python SDK, use fetch()
95 | Query.prototype.limit = function (limit) {
96 | if (!this.fetchOptions) {
97 | this.fetchOptions = JFetchOptionsBuilder.withLimit(limit);
98 | } else {
99 | this.fetchOptions.limit(limit);
100 | }
101 | return this;
102 | }
103 |
104 | // FIXME: Deprecate, not available in the Python SDK, use fetch()
105 | Query.prototype.offset = function (offset) {
106 | if (!this.fetchOptions) throw Error("Call .limit(n) before calling .offset(n)");
107 | this.fetchOptions = this.fetchOptions.offset(offset);
108 | return this;
109 | }
110 |
111 | /**
112 | * Set the start of this query to the given serialized cursor.
113 | *
114 | * When executed, this query will start from the next result for a previous
115 | * invocation of a similar query.
116 | *
117 | * Returns:
118 | * This Query instance, for chaining.
119 | */
120 | Query.prototype.withCursor = function (cursor) {
121 | if (cursor) {
122 | var jcursor = JCursor.fromWebSafeString(cursor);
123 |
124 | if (!this.fetchOptions) {
125 | this.fetchOptions = JFetchOptionsBuilder.withCursor(jcursor);
126 | } else {
127 | this.fetchOptions.cursor(jcursor);
128 | }
129 | }
130 |
131 | return this;
132 | }
133 |
134 | var GREATER_THAN_OR_EQUAL = JQuery.FilterOperator.GREATER_THAN_OR_EQUAL,
135 | LESS_THAN = JQuery.FilterOperator.LESS_THAN;
136 |
137 | /**
138 | * API extension.
139 | * Emulates the JDO startsWith operator.
140 | * http://groups.google.com/group/google-appengine-java/browse_thread/thread/958851cc674d0c70/7403586fae9ffe20?lnk=gst&q=startswith#7403586fae9ffe20
141 | */
142 | Query.prototype.startsWith = function (property, value) {
143 | this.query.addFilter(property, GREATER_THAN_OR_EQUAL, value);
144 | this.query.addFilter(property, LESS_THAN, value + "\ufffd");
145 | return this;
146 | }
147 |
148 | Query.prototype._prepare = function (args) {
149 | if (!this.prepared) {
150 | if (args && args.rpc) {
151 | this.prepared = args.rpc.getDatastore().prepare(this.query);
152 | } else {
153 | // THINK: remove this special case.
154 | this.prepared = JDatastore.prepare(this.query);
155 | }
156 | }
157 | }
158 |
159 | /**
160 | * Iterator for this query.
161 | *
162 | * If you know the number of results you need, consider fetch() instead,
163 | * or use a GQL query with a LIMIT clause. It's more efficient.
164 | *
165 | * Returns:
166 | * Iterator for this query.
167 | */
168 | Query.prototype.run = function (args) {
169 | this._prepare(args);
170 |
171 | if (this.fetchOptions) {
172 | var iterator = this.prepared.asQueryResultIterator(this.fetchOptions);
173 | } else {
174 | var iterator = this.prepared.asQueryResultIterator();
175 | }
176 |
177 | // Python SDK compiled query, used for cursor calculation.
178 | this._last_raw_query = iterator;
179 |
180 | return iterator;
181 | }
182 |
183 | /**
184 | *
185 | */
186 | Query.prototype.get = function (args) {
187 | this._prepare(args);
188 |
189 | var e = this.prepared.asSingleEntity();
190 |
191 | if (e) {
192 | if (this.query.isKeysOnly()) {
193 | return e.getKey();
194 | } else {
195 | return entityToObject(e);
196 | }
197 | }
198 | }
199 |
200 | /**
201 | * Executes the query, then returns the results.
202 | *
203 | * The limit and offset arguments control how many results are fetched from the
204 | * datastore, and how many are returned by the fetch() method:
205 | * - The datastore fetches offset + limit results to the application. The first
206 | * offset results are not skipped by the datastore itself.
207 | * - The fetch() method skips the first offset results, then returns the rest
208 | * (limit results).
209 | * - The query has performance characteristics that correspond linearly with the
210 | * offset amount plus the limit.
211 | */
212 | Query.prototype.fetch = function (limit, args) {
213 | if (limit) {
214 | this.limit(limit);
215 | if (args && args.offset) {
216 | this.offset(args.offset);
217 | }
218 | }
219 |
220 | var iterator = this.run(args),
221 | objects = [];
222 |
223 | if (this.query.isKeysOnly()) {
224 | for (var e in Iterator(iterator)) {
225 | objects.push(Key.fromDatastoreKey(e.getKey()));
226 | }
227 | } else {
228 | for (var e in Iterator(iterator)) {
229 | objects.push(entityToObject(e));
230 | }
231 | }
232 |
233 | return objects;
234 | }
235 |
236 | /**
237 | * Get a serialized cursor for an already executed query.
238 | *
239 | * The returned cursor effectively lets a future invocation of a similar query
240 | * to begin fetching results immediately after the last returned result from
241 | * this query invocation.
242 | *
243 | * Returns:
244 | * A base64-encoded serialized cursor.
245 | */
246 | Query.prototype.cursor = function () {
247 | if (this._last_raw_query) {
248 | return this._last_raw_query.getCursor().toWebSafeString();
249 | }
250 | }
251 |
252 | Query.prototype.keys = function (limit, args) {
253 | this.query.setKeysOnly();
254 | return this.fetch(limit, args);
255 | }
256 |
257 | /**
258 | * API Extension.
259 | */
260 | Query.prototype.forEach = function (func, args) {
261 | var iterator = this.run(args);
262 |
263 | if (this.query.isKeysOnly()) {
264 | for (var e in Iterator(iterator)) {
265 | func(Key.fromDatastoreKey(e.getKey()));
266 | }
267 | } else {
268 | for (var e in Iterator(iterator)) {
269 | func(entityToObject(e));
270 | }
271 | }
272 | }
273 |
274 | Query.prototype.count = function (limit, args) {
275 | if (limit) {
276 | this.limit(limit);
277 | }
278 |
279 | this._prepare(args);
280 |
281 | return Number(this.prepared.countEntities());
282 | }
283 |
--------------------------------------------------------------------------------
/lib/google/appengine/ext/db/stats.js:
--------------------------------------------------------------------------------
1 | // Under construction, not working yet.
2 |
3 | /**
4 | * @fileoverview Models to be used when accessing app specific datastore usage statistics.
5 | *
6 | * These entities cannot be created by users, but are populated in the
7 | * application's datastore by offline processes run by the Google App Engine team.
8 | */
9 |
10 | var db = require("google/appengine/ext/db");
11 |
12 | /**
13 | * Base Statistic Model class.
14 | *
15 | * The 'bytes' attribute represents the total number of bytes taken up in the
16 | * datastore for the statistic instance. The 'count' attribute is the
17 | * total number of occurrences of the statistic in the datastore. The
18 | * 'timestamp' is when the statistic instance was written to the datastore.
19 | *
20 | * @constructor
21 | */
22 | var BaseStatistic = new db.Model.extend({
23 | _kind: "__BaseStatistic__",
24 | bytes: new db.IntegerProperty(),
25 | count: new db.IntegerProperty(),
26 | timestamp: new db.DateTimeProperty()
27 | });
28 |
29 | /**
30 | * Base Statistic Model class for stats associated with kinds.
31 | *
32 | * The 'kindName' attribute represents the name of the kind associated with the
33 | * statistic instance.
34 | *
35 | * @constructor
36 | */
37 | var BaseKindStatistic = BaseStatistic.extend({
38 | _kind: "__BaseKindStatistic__",
39 | kindName: new db.StringProperty()
40 | });
41 |
42 | /**
43 | * An aggregate of all entities across the entire application.
44 | *
45 | * This statistic only has a single instance in the datastore that contains the
46 | * total number of entities stored and the total number of bytes they take up.
47 | *
48 | * @constructor
49 | */
50 | exports.GlobalStat = BaseStatistic.extend({
51 | _kind: "__Stat_Total__"
52 | });
53 |
54 | /**
55 | * An aggregate of all entities at the granularity of their Kind.
56 | *
57 | * There is an instance of the KindStat for every Kind that is in the
58 | * application's datastore. This stat contains per-Kind statistics.
59 | *
60 | * @constructor
61 | */
62 | exports.KindStat = BaseKindStatistic.extend({
63 | _kind: "__Stat_Kind__"
64 | });
65 |
66 | /**
67 | * Statistics of the number of root entities in the datastore by Kind.
68 | *
69 | * There is an instance of the KindRootEntityState for every Kind that is in the
70 | * application's datastore and has an instance that is a root entity. This stat
71 | * contains statistics regarding these root entity instances.
72 | *
73 | * @constructor
74 | */
75 | exports.KindRootEntityStat = BaseKindStatistic.extend({
76 | _kind: "__Stat_Kind_isRootEntity__"
77 | });
78 |
79 | /**
80 | * An aggregate of all properties across the entire application by type.
81 | *
82 | * There is an instance of the PropertyTypeStat for every property type
83 | * (google.appengine.api.datastore_types._PROPERTY_TYPES) in use by the
84 | * application in its datastore.
85 | *
86 | * @constructor
87 | */
88 | exports.PropertyTypeStat = BaseStatistic.extend({
89 | _kind: "__Stat_PropertyType__",
90 | propertyType: new db.StringProperty()
91 | });
92 |
93 | /**
94 | * Statistics on (kind, propertyType) tuples in the app's datastore.
95 | *
96 | * There is an instance of the KindPropertyTypeStat for every
97 | * (kind, propertyType) tuple in the application's datastore.
98 | *
99 | * @constructor
100 | */
101 | exports.KindPropertyTypeStat = BaseKindStatistic.extend({
102 | _kind: "__Stat_PropertyType_Kind__",
103 | propertyType: new db.StringProperty()
104 | });
105 |
106 | /**
107 | * Statistics on (kind, property_name) tuples in the app's datastore.
108 | *
109 | * There is an instance of the KindPropertyNameStat for every
110 | * (kind, property_name) tuple in the application's datastore.
111 | *
112 | * @constructor
113 | */
114 | exports.KindPropertyNameStat = BaseKindStatistic.extend({
115 | _kind: "__Stat_PropertyName_Kind__",
116 | propertyName: new db.StringProperty()
117 | });
118 |
119 | /**
120 | * Statistic on (kind, propertyName, propertyType) tuples in the datastore.
121 | *
122 | * There is an instance of the KindPropertyNamePropertyTypeStat for every
123 | * (kind, propertyName, propertyType) tuple in the application's datastore.
124 | *
125 | * @constructor
126 | */
127 | exports.KindPropertyNamePropertyTypeStat = BaseKindStatistic.extend({
128 | _kind: "__Stat_PropertyType_PropertyName_Kind__",
129 | propertyType: new db.StringProperty(),
130 | propertyName: new db.StringProperty()
131 | });
132 |
133 |
--------------------------------------------------------------------------------
/lib/google/appengine/ext/db/utils.js:
--------------------------------------------------------------------------------
1 | // TODO: try to remove this file.
2 |
3 | var JKey = Packages.com.google.appengine.api.datastore.Key,
4 | JEntity = Packages.com.google.appengine.api.datastore.Entity;
5 |
6 | var Key = require("google/appengine/api/datastore/types").Key;
7 |
8 | // A map from kind strings to kind constructors.
9 | var kindMap = exports.kindMap = {};
10 |
11 | /**
12 | * Helper to copy properties from a trait/mixin.
13 | */
14 | exports.copyProperties = function(base, src) {
15 | var properties = base.model.properties;
16 |
17 | for (var name in src) {
18 | var srcproperty = src[name];
19 | var property = properties[name] = new srcproperty.constructor;
20 | for (var i in srcproperty) property[i] = srcproperty[i];
21 | property.name = name;
22 | property.init(base);
23 | }
24 | }
25 |
26 | /**
27 | * Convert a GAE DataStore entity to an object.
28 | * Uses the metadata in the constructor.model to convert the object properties.
29 | */
30 | exports.entityToObject = function(entity) {
31 | var constructor = kindMap[entity.getKind()];
32 | return constructor.fromEntity(entity);
33 | }
34 |
35 | /**
36 | * Convert an object to a GAE DataStore entity.
37 | * Uses the metadata in the constructor.model to convert the object properties.
38 | */
39 | exports.objectToEntity = function(obj) {
40 | return obj._toEntity();
41 | }
42 |
43 |
--------------------------------------------------------------------------------
/lib/google/appengine/ext/deferred.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gmosx/appengine_js/10677f56d8aae95f4c2d7ef4b560f21bc18913eb/lib/google/appengine/ext/deferred.js
--------------------------------------------------------------------------------
/lib/google/appengine/ext/jsgi/namespace.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Enable request namespace middelware.
3 | */
4 |
5 | var enableRequestNamespace = require("google/appengine/api/namespace-manager").enableRequestNamespace;
6 |
7 | /**
8 | * Namespace JSGI middleware.
9 | *
10 | * Set the default namespace to the Google Apps domain referring
11 | * this request.
12 | *
13 | * Calling this function will set the default namespace to the
14 | * Google Apps domain that was used to create the url used for
15 | * this request and only for the current request and only if the
16 | * current default namespace is unset.
17 | *
18 | * @see http://code.google.com/appengine/docs/java/multitenancy/multitenancy.html#Setting_the_Current_Namespace
19 | * @see http://code.google.com/appengine/docs/python/multitenancy/multitenancy.html#Setting_the_Current_Namespace
20 | */
21 | exports.Namespace = exports.middleware = function (app) {
22 | return function (request) {
23 | enableRequestNamespace();
24 | return app(request);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/lib/google/appengine/ext/remote-api.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview An apiproxy stub that calls a remote handler via HTTP.
3 | *
4 | * This allows easy remote access to the App Engine datastore, and potentially any
5 | * of the other App Engine APIs, using the same interface you use when accessing
6 | * the service locally.
7 | *
8 | * UNDER CONSTRUCTION
9 | */
10 |
11 | /**
12 | * Does necessary setup to allow easy remote access to App Engine APIs.
13 | *
14 | * Either servername must be provided or app_id must not be None. If app_id
15 | * is None and a servername is provided, this function will send a request
16 | * to the server to retrieve the app_id.
17 | */
18 | exports.configureRemoteApi = function (appId, path) {
19 | }
20 |
21 | exports.configureRemoteDatastore = exports.configureRemoteApi;
22 |
--------------------------------------------------------------------------------
/lib/google/appengine/ext/webapp.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview The webapp framework is a simple web application framework you can
3 | * use for developing JavaScript web applications for App Engine. webapp
4 | * is compatible with the JSGI standard for JavaScript web application
5 | * containers. webapp provides an easy way to get started developing apps
6 | * for App Engine.
7 | */
8 |
9 | /**
10 | * Abstraction for an HTTP request.
11 | * Properties:
12 | * uri: the complete URI requested by the user
13 | * scheme: 'http' or 'https'
14 | * host: the host, including the port
15 | * path: the path up to the ';' or '?' in the URL
16 | * parameters: the part of the URL between the ';' and the '?', if any
17 | * query: the part of the URL after the '?'
18 | *
19 | * You can access parsed query and POST values with the get() method; do not
20 | * parse the query string yourself.
21 | *
22 | * @constructor
23 | */
24 | var Request = exports.Request = function () {
25 | }
26 |
27 | /**
28 | * Abstraction for an HTTP response.
29 | *
30 | * Properties:
31 | * out: file pointer for the output stream
32 | * headers: wsgiref.headers.Headers instance representing the output headers
33 | *
34 | * @constructor
35 | */
36 | var Response = exports.Response = function () {
37 | }
38 |
39 | /**
40 | * @constructor
41 | */
42 | var RequestHandler = exports.RequestHandler = function () {
43 | }
44 |
45 | /**
46 | * @constructor
47 | */
48 | var JSGIApplication = exports.JSGIApplication = function (urlMapping, debug) {
49 | }
50 |
51 |
--------------------------------------------------------------------------------
/lib/google/appengine/ext/webapp/blobstore-handlers.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gmosx/appengine_js/10677f56d8aae95f4c2d7ef4b560f21bc18913eb/lib/google/appengine/ext/webapp/blobstore-handlers.js
--------------------------------------------------------------------------------
/lib/google/appengine/ext/webapp/mail-handlers.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gmosx/appengine_js/10677f56d8aae95f4c2d7ef4b560f21bc18913eb/lib/google/appengine/ext/webapp/mail-handlers.js
--------------------------------------------------------------------------------
/lib/google/appengine/ext/webapp/template.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gmosx/appengine_js/10677f56d8aae95f4c2d7ef4b560f21bc18913eb/lib/google/appengine/ext/webapp/template.js
--------------------------------------------------------------------------------
/lib/google/appengine/ext/webapp/util.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gmosx/appengine_js/10677f56d8aae95f4c2d7ef4b560f21bc18913eb/lib/google/appengine/ext/webapp/util.js
--------------------------------------------------------------------------------
/lib/google/appengine/ext/webapp/xmpp-handlers.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gmosx/appengine_js/10677f56d8aae95f4c2d7ef4b560f21bc18913eb/lib/google/appengine/ext/webapp/xmpp-handlers.js
--------------------------------------------------------------------------------
/lib/google/appengine/logging.js:
--------------------------------------------------------------------------------
1 | var JLogger = Packages.java.util.logging.Logger,
2 | jlog = JLogger.getLogger("app");
3 |
4 | exports.info = function(msg) {
5 | jlog.info(msg);
6 | }
7 |
8 | exports.warning = function(msg) {
9 | jlog.warning(msg);
10 | }
11 |
--------------------------------------------------------------------------------
/lib/google/appengine/runtime.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Exception raised when the request reaches its overall time limit.
3 | *
4 | * Not to be confused with runtime.apiproxy_errors.DeadlineExceededError.
5 | * That one is raised when individual API calls take too long.
6 | */
7 | // exports.DeadlineExceededError = Object.create(Error.prototype);
8 |
9 | exports.DeadlineExceededError = Packages.com.google.apphosting.api.DeadlineExceededError;
10 |
--------------------------------------------------------------------------------
/lib/google/appengine/tools/development/sdk.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview SDK helpers for development.
3 | */
4 |
5 | var fs = require("fs");
6 |
7 | exports.root = system.env["APPENGINE_JAVA_SDK"];
8 |
9 | if (!exports.root) {
10 | print("Please set the environment variable 'APPENGINE_JAVA_SDK' to the sdk root directory")
11 | }
12 |
13 | exports.path = function (path) {
14 | return fs.join(exports.root, path);
15 | }
16 |
--------------------------------------------------------------------------------
/lib/google/appengine/tools/development/testing.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Local Unit Testing support.
3 | *
4 | * Since there is no Python equivalent at the moment, we provive the
5 | * appengine-jruby API. The lower level Java API is also exposed.
6 | *
7 | * http://code.google.com/appengine/docs/java/tools/localunittesting.html
8 | * http://code.google.com/p/appengine-jruby/source/browse/appengine-apis/lib/appengine-apis/testing.rb
9 | */
10 |
11 | var sdk = require("google/appengine/tools/development/sdk");
12 |
13 | // Include runtime and testing jars.
14 | addToClasspath(sdk.path("/lib/impl/appengine-api.jar"));
15 | addToClasspath(sdk.path("/lib/impl/appengine-api-labs.jar"));
16 | addToClasspath(sdk.path("/lib/impl/appengine-api-stubs.jar"));
17 | addToClasspath(sdk.path("/lib/testing/appengine-testing.jar"));
18 |
19 | // The Java API.
20 | var jtesting = exports.java = Packages.com.google.appengine.tools.development.testing;
21 |
22 | var serviceMap = {
23 | blobstore: jtesting.LocalBlobstoreServiceTestConfig,
24 | datastore: jtesting.LocalDatastoreServiceTestConfig,
25 | images: jtesting.LocalImagesServiceTestConfig,
26 | mail: jtesting.LocalMailServiceTestConfig,
27 | memcache: jtesting.LocalMemcacheServiceTestConfig,
28 | taskqueue: jtesting.LocalTaskQueueTestConfig,
29 | urlfetch: jtesting.LocalURLFetchServiceTestConfig,
30 | users: jtesting.LocalUserServiceTestConfig,
31 | xmpp: jtesting.LocalXMPPServiceTestConfig
32 | }
33 |
34 | /**
35 | * Local service test helper.
36 | * @constructor
37 | * @param {...String} var_services An variable number of service names to setyp,
38 | * if no arguments are passed, config all services.
39 | */
40 | var Helper = exports.Helper = function (var_services) {
41 | var services = Array.prototype.splice.call(arguments, 0);
42 | if (services.length == 0) services = Object.keys(serviceMap);
43 | /*
44 | services = services || Object.keys(serviceMap);
45 | */
46 | this.config = {};
47 | var self = this;
48 | this._jhelper = new jtesting.LocalServiceTestHelper(
49 | services.map(function (s) {
50 | self.config[s] = new serviceMap[s]();
51 | return self.config[s];
52 | })
53 | );
54 | }
55 |
56 | Helper.prototype.setup = function () {
57 | this._jhelper.setUp();
58 | }
59 |
60 | Helper.prototype.teardown = function () {
61 | this._jhelper.tearDown();
62 | }
63 |
64 | /**
65 | * The standard helper.
66 | */
67 | exports.helper;
68 |
69 | /**
70 | * Standard unit test setup, enables all services.
71 | */
72 | exports.setup = exports.setUp = function () {
73 | exports.helper = exports.helper || new Helper();
74 | exports.helper.setup();
75 | }
76 |
77 | /**
78 | * Standard unit test tear down.
79 | */
80 | exports.teardown = exports.tearDown = function () {
81 | if (exports.helper) {
82 | exports.helper.teardown();
83 | } else {
84 | throw new Error("Standard helper is not setup");
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/lib/google/appengine/utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileoverview Various internal utilities, not part of the public API.
3 | */
4 |
5 | exports.defineError = function (name, Zuper) {
6 | var ctor = function (message) {
7 | // var stack = (new Error).stack.split("\n").slice(1);
8 | // [undef, undef, this.fileName, this.lineNumber] = /^(.*?)@(.*?):(.*?)$/.exec(stack[1]);
9 | // this.stack = stack.join("\n");
10 | this.message = message;
11 | }
12 |
13 | Zuper = Zuper || Error;
14 |
15 | ctor.prototype = Object.create(Zuper.prototype);
16 | ctor.prototype.constructor = ctor;
17 | ctor.prototype.name = name;
18 |
19 | return ctor;
20 | }
21 |
22 | exports.extend = function (Klass, Zuper) {
23 | Klass.prototype = Object.create(Zuper.prototype);
24 | Klass.prototype.constructor = Klass;
25 | }
26 |
27 | /**
28 | * Extract the datastore key from a Model or Key.
29 | */
30 | exports.datastoreKey = function (model_or_key) {
31 | throw new Error("not implemented");
32 | }
33 |
--------------------------------------------------------------------------------
/lib/google/apphosting/api/apiproxy.js:
--------------------------------------------------------------------------------
1 | var ApiProxy = exports.ApiProxy = {};
2 |
3 | ApiProxy.getCurrentEnvironment = function () {
4 | }
5 |
--------------------------------------------------------------------------------
/lib/mapreduce/operation.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gmosx/appengine_js/10677f56d8aae95f4c2d7ef4b560f21bc18913eb/lib/mapreduce/operation.js
--------------------------------------------------------------------------------
/lib/mapreduce/operation/counters.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gmosx/appengine_js/10677f56d8aae95f4c2d7ef4b560f21bc18913eb/lib/mapreduce/operation/counters.js
--------------------------------------------------------------------------------
/lib/mapreduce/operation/db.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gmosx/appengine_js/10677f56d8aae95f4c2d7ef4b560f21bc18913eb/lib/mapreduce/operation/db.js
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "appengine",
3 | "description": "A port of the Python Google App Engine SDK to JavaScript",
4 | "keywords": ["google", "appengine", "cloud", "datastore"],
5 | "maintainers": [
6 | {
7 | "name": "George Moschovitis",
8 | "email": "george.moschovitis@gmail.com",
9 | "web": "http://www.gmosx.com"
10 | }
11 | ],
12 | "contributors": [
13 | {
14 | "name": "George Moschovitis",
15 | "email": "george.moschovitis@gmail.com",
16 | "web": "http://www.gmosx.com"
17 | },
18 | {
19 | "name": "Christoph Dorn",
20 | "email": "christoph@christophdorn.com",
21 | "web": "http://www.christophdorn.com"
22 | }
23 | ],
24 | "lib": "lib",
25 | "lean": {
26 | "include": [
27 | "lib/**/*",
28 | "package.json"
29 | ]
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/test/google/appengine/ext/db/model.js:
--------------------------------------------------------------------------------
1 | var assert = require("assert"),
2 | Helper = require("google/appengine/tools/development/testing").Helper,
3 | helper = new Helper("datastore");
4 |
5 | var db = require("google/appengine/ext/db");
6 |
7 | var Base = db.Model.extend("Base", {
8 | title: new db.StringProperty(),
9 | level: new db.FloatProperty()
10 | })
11 |
12 | var Child = Base.extend("Child", {
13 | another: new db.StringProperty()
14 | })
15 |
16 | exports.testPut = function () {
17 | helper.setup();
18 | var b = new Base({title: "hello", level: 2});
19 | b.put();
20 | var c = Base.get(b.key());
21 | assert.equal(c.title, "hello");
22 | helper.teardown();
23 | }
24 |
25 | exports.testModelExtension = function () {
26 | helper.setup();
27 | var props = Child.properties();
28 | assert.ok(props.level);
29 | helper.teardown();
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/test/run.js:
--------------------------------------------------------------------------------
1 | exports.testModel = require("./google/appengine/ext/db/model");
2 |
3 | if (module === require.main) {
4 | require("test").run(exports);
5 | system.exit(0);
6 | }
7 |
--------------------------------------------------------------------------------