Commit Graph

632 Commits

Author SHA1 Message Date
Raymond Feng 2bf69a1171 Expose the `filter` argument for findById 2015-04-16 15:02:41 -07:00
Edmond Lau ef7c1439b6 fixed the missing '.' in various description fields. 2015-04-15 09:47:38 -04:00
Miroslav Bajtoš bd973def63 Merge pull request #1294 from strongloop/feature/conflict-resolution-access-control
Conflict resolution and Access control
2015-04-14 09:36:40 +02:00
Miroslav Bajtoš cf2acb3cd2 Conflict resolution and Access control
Add end-to-end unit-tests verifying enforcement of access control during
conflict resolution.

Implement two facade methods providing REST API for Change methods used
by conflict resolution:

    PersistedModel.findLastChange
    GET /api/{model.pluralName}/{id}/changes/last

    PersistedModel.updateLastChange
    PUT /api/{model.pluralName}/{id}/changes/last

By providing these two methods on PersistedModel, replication users
don't have to expose the Change model via the REST API. What's even
more important, these two methods use the same set of ACL rules
as other (regular) PersistedModel methods.

Rework `Conflict.prototype.changes()` and `Conflict.prototype.resolve()`
to use these new facade methods.

Implement a new method `Conflict.prototype.swapParties()` that provides
better API for the situation when a conflict detected in Remote->Local
replication should be resolved locally (i.e. in the replication target).
2015-04-14 08:23:24 +02:00
Raymond Feng b4c487b191 Fix the typo 2015-04-10 11:03:55 -07:00
Miroslav Bajtoš 28acffd7dd Fix PersistedModel._defineChangeModel
Correctly handle the case when the model is attached multiple times
during the lifecycle, this happens because `loopback.createModel`
always makes an attempt to auto-attach.
2015-04-08 11:55:03 +02:00
Miroslav Bajtoš 9c5fe088e3 AccessControl for change replication
1) Add integration tests running change replication over REST to verify
that access control at model level is correctly enforced.

2) Implement a new access type "REPLICATE" that allows principals
to create new checkpoints, even though they don't have full WRITE
access to the model. Together with the "READ" permission, these
two types allow principals to replicate (pull) changes from the server.

Note that anybody having "WRITE" access type is automatically
granted "REPLICATE" type too.

3) Add a new model option "enableRemoteReplication" that exposes
replication methods via strong remoting, but does not configure
change rectification. This option should be used the clients
when setting up Remote models attached to the server via the remoting
connector.
2015-04-07 19:53:58 +02:00
Miroslav Bajtoš b61fae58f6 Merge pull request #1272 from strongloop/feature/after-remote-error-hook
Model.afterRemoteError hook
2015-04-07 09:47:51 +02:00
Ritchie Martori b9170751bc Add support for app level Model isolation
- `loopback.registry` is now a true global registry
 - `app.registry` is unique per app object
 - `Model.registry` is set when a Model is created using any registry method
 - `loopback.localRegistry` and `loopback({localRegistry: true})` when set to `true` this will create a `Registry` per `Application`. It defaults to `false`.
2015-04-03 11:48:45 -07:00
Miroslav Bajtoš dd83be99f0 Implement ModelCtor.afterRemoteError 2015-04-03 10:31:03 +02:00
Miroslav Bajtoš a71c8253e2 Code cleanup, add Model._runWhenAttachedToApp 2015-04-03 10:06:49 +02:00
Miroslav Bajtoš c72c134d80 Refactor Model and PersistedModel registration
Modify the files to export a model factory function accepting
a `registry` argument. This is a preparation step for per-application
models - see #1212.
2015-04-03 09:26:19 +02:00
Miroslav Bajtoš 8493ede19e Add missing error handlers to checkpoints() 2015-04-02 14:14:32 +02:00
Rand McKinney 8a1fe0b744 Fix where param format 2015-04-01 11:29:35 -07:00
Miroslav Bajtoš 63e2f4b134 Improve error handling in replication
Deprecate `Change.handleError`, it was used inconsistenly for a subset
of possible errors only. Rework all `Change` methods to always report
all errors to the caller via the callback.

Rework `PersistedModel` to report change-tracking errors via the
existing method `PersistedModel.handleChangeError`. This method
can be customized on a per-model basis to provide different error
handling.

The default implementation emits `error` event on the model class,
users can attach an event listener that can provide a custom error
handler.

NOTE: Unhandled `error` events crash the application by default.
2015-03-30 11:07:53 +02:00
Miroslav Bajtoš 2aa09ba574 Add `loopback.runInContext`
Refactor the core implementation of current context from
server/middleware/context.js into server/current-context.js.

Expose new public API:
 - loopback.runInContext
 - loopback.createContext
2015-03-27 19:12:17 +01:00
Miroslav Bajtoš ade5d5ea21 Merge pull request #1246 from strongloop/fix/browser-bundle-size
Import subset of underscore.string scripts only
2015-03-27 08:51:59 +01:00
Miroslav Bajtoš 2ca621e597 Document the new third callback arg of replicate() 2015-03-26 19:10:00 +01:00
Miroslav Bajtoš caf53f72c1 Fix API doc for updateAll/deleteAll
Based on changes made in
  https://github.com/strongloop/loopback/issues/1167
  https://github.com/strongloop/loopback-datasource-juggler/pull/540
2015-03-26 19:06:56 +01:00
Esco Obong 1993338c0b Merge branch 'master' of https://github.com/strongloop/loopback 2015-03-25 16:45:58 -04:00
Miroslav Bajtoš 7528cbb712 Import subset of underscore.string scripts only
Require individual methods like `classify` instead of the whole module.
This reduces the size of the browser bundle from ~27kb down to ~2kb.
2015-03-25 14:02:16 +01:00
Miroslav Bajtoš cadb5e4524 Use `ctx.instance` provided by "after delete" hook
Use the recently added context property `ctx.instance` to improve
the accuracy of the algorithm detecting whether a single or
multiple models were deleted.
2015-03-23 11:49:47 +01:00
Miroslav Bajtoš 87940a4b58 Detect 3rd-party changes made during replication
Modify `Change.diff()` to include current data revision in each
delta reported back. The current data revision is stored in
`delta.prev`.

Modify `PersistedModel.bulkUpdate()` to check that the current data
revision matches `delta.prev` and report a conflict if a third party
has modified the database under our hands.

Fix `Change` implementation and tests so that they are no longer
attempting to create instances with duplicate ids.
(This used to work because the memory connector was silently
converting such requests to updateOrCreate/findOrCreate.)
2015-03-20 08:19:59 +01:00
Esco Obong 8cc558a991 consolidate Role methods roles, applications, and users into one, add query param to allow for pagination and restricting fields 2015-03-13 11:50:30 -04:00
Miroslav Bajtoš 76d9244448 Prevent more kinds of false replication conflicts
Rework the Change model to merge changes made within the same
Checkpoint.

Rework `replicate()` to run multiple iteration until there were no
changes replicated. This ensures that the target model is left in
a clean state with no pending changes associated with the latest
(current) checkpoint.
2015-03-05 14:15:02 +01:00
Miroslav Bajtoš b381c5df7e Add more debug logs to replication 2015-03-04 15:00:53 +01:00
Miroslav Bajtoš 628e3a30ca Return new checkpoints in callback of replicate()
Extend `PersistedModel.replicate` to pass the newly created checkpoints
as the third callback argument.

The typical usage of these values is to pass them as the `since`
argument of the next `replicate()` call.

    global.since = -1;

    function sync(cb) {
      LocalModel.replicate(
        since,
        RemoteModel,
        function(err, conflicts, cps)
          if (err) return cb(err);
          if (!conflicts.length) {
            since = cps;
            return cb();
          }
          // resolve conflicts and try again
        });
    }
2015-03-03 19:37:11 +01:00
Miroslav Bajtoš 53ebddfa9f Create a remote checkpoint during replication too
Before this change, in the case of a one-way replication, the remote
checkpoint was never updated, thus the "diff" step had to check
all changes made through the whole history.

This commit fixes the problem by creating a new remote checkpoint
at the same time when a local checkpoint is created.

It is important to create the new checkpoint before the replication is
started to prevent a race condition where a remote change can end up
being associated with an already replicated checkpoint.
2015-03-03 19:37:11 +01:00
Miroslav Bajtoš 2dc230b7cf Replication: fix checkpoint-related race condition
Rework the "replicate()" to create a new source checkpoint as the first
step, so that any changes made in parallel will be associated with
the next checkpoint.

Before this commit, there was a race condition where a change might
end up being associated with the already-replicated checkpoint and thus
not picked up by the next replication run.
2015-03-03 19:37:11 +01:00
Miroslav Bajtoš 2f9cf115c3 Support different "since" for source and target
Modify `PersistedModel.replicate` to allow consumers to provide
different "since" values to be used for the local and remote changes.

Make the "since" filters consistent and include the "since" value
in the range via `gte`. Before this commit, the local query used
`gt` and the remote query used `gte`.
2015-03-03 14:40:59 +01:00
Miroslav Bajtoš 610767b6c8 Change tracking requires a string id set to GUID
Print a deprecation warning when a persisted model is tracking changes
but does not have a client-generated unique string id property (GUID).
2015-03-02 18:16:18 +01:00
Raymond Feng 9a0267e87a Add a workaround to avoid conflicts with NewRelic 2015-02-25 09:47:18 -08:00
Miroslav Bajtoš 3d977f3e68 Merge pull request #1116 from strongloop/fix/change-detection
Fix change detection & tracking
2015-02-24 08:24:27 +01:00
crandmck a82b33ec5c Add docs for settings per #1069 2015-02-23 13:13:52 -08:00
Miroslav Bajtoš 702ecc6f72 Fix change detection & tracking
Add unit-tests to verify that all DAO methods correctly create change
records.

Rework the change detection to use the new operation hooks, this fixes
the bugs where operations like "updateOrCreate" did not update change
records.
2015-02-20 19:28:33 +01:00
Ritchie Martori c7bead4107 Minor doc fix 2015-02-20 09:16:09 -08:00
Miroslav Bajtoš c2236c393b Upgrade jscs to ~1.11 via grunt-jscs ^1.5 2015-02-20 15:31:15 +01:00
Raymond Feng fb88d98870 Remove trailing spaces 2015-02-18 09:13:47 -08:00
crandmck ea35a2f48d Fix API docs per #1041 2015-02-10 15:18:34 -08:00
crandmck 538e9f0c62 Fix API docs to add proper callback doc per #1041 2015-02-10 15:13:55 -08:00
Ritchie Martori 4b6dcd6271 Document user settings 2015-02-04 14:09:01 -08:00
Raymond Feng 4bf5970f9c Map not found to 404 for hasOne 2015-02-03 11:13:15 -08:00
Raymond Feng bbba49d99c Enable remoting for hasOne relations 2015-01-29 22:33:34 -08:00
Miroslav Bajtoš e4adbf8366 Merge pull request #1038 from strongloop/fix/remove-usage-of-deprecated-req.param
Remove usages of deprecated `req.param()`
2015-01-27 09:42:43 +01:00
Rand McKinney 68c0f1e15a Document options for persistedmodel.save() 2015-01-23 09:54:14 -08:00
crandmck fc31f37a83 Fix JSdocs per #888 2015-01-22 17:01:31 -08:00
Miroslav Bajtoš a77b3bbac7 Remove usages of deprecated `req.param()`
Express has recently deprecated `req.param()` to force developers
to be explicit about the source of the value. To avoid deprecation
warnings, this commit replaces all calls of `req.param()` with a
simplified inline version.
2015-01-21 19:27:53 +01:00
Ron Edgecomb a028d9d198 Add error code property to known error responses.
Enhance the error objects with a `code` property containing
a machine-readable string code describing the error, for example
INVALID_TOKEN or USER_NOT_FOUND.

Also improve 404 error messages to include the model name.
2015-01-21 19:04:47 +01:00
bitmage c28698c1ba don't send queries to the DB when no changes are detected 2015-01-10 11:28:55 -07:00
Raymond Feng 16210e0f79 Allow accessType per remote method 2015-01-07 14:40:09 -08:00
Clark Wang 94b2a45a6c fix nestRemoting is nesting hooks from other relations
Signed-off-by: Clark Wang <clark.wangs@gmail.com>
2015-01-01 15:26:58 +08:00
Miroslav Bajtoš 4744aa6920 server-app: make _sortLayersByPhase stable
Fix the phase-sorting algorithm to use a stable sorting algorithm,
since the built-in `Array.prototype.sort` is not stable.
2014-12-15 08:14:26 +01:00
Miroslav Bajtoš 84af4194fb Rework phased middleware, fix several bugs
Bugs fixed:

 - express helpers like `req.get` are now available in middleware
   handlers registered via `app.middleware`

 - `req.url` does not include the mountpath prefix now, this is
   consistent with the behaviour of `app.use`

The implementation of phased middleware was completely rewritten.
 - We no longer use Phase and PhaseList objects from loopback-phase.
 - Handler functions are registered via the `Layer` mechanism used by
   express router.
 - The app keeps the layers sorted according to phases.
2014-12-12 13:25:35 +01:00
Clark Wang 9c147f1b25 fix jshint errors
Signed-off-by: Clark Wang <clark.wangs@gmail.com>
2014-12-10 19:43:55 +08:00
Clark Wang 7a3e254403 test if cb exists
Signed-off-by: Clark Wang <clark.wangs@gmail.com>
2014-12-10 19:03:48 +08:00
Clark Wang b204367aa6 fix nested remoting function throwing error will crash app
Signed-off-by: Clark Wang <clark.wangs@gmail.com>
2014-12-10 12:04:56 +08:00
Ryan Graham 7c0b2e83eb Remove unused underscore dependency 2014-12-02 21:53:30 -08:00
Clark Wang a12c2ece28 Prepend slash for nested remoting paths
Fix remoting paths of relation methods to correctly show
in API Explorer.
2014-11-27 09:56:44 +01:00
Raymond Feng a9b4df1f2d Expose more loopback middleware for require 2014-11-19 11:32:22 -08:00
Miroslav Bajtoš 2baa4b03a3 Scope app middleware to a list of paths
Add a new argument to `app.middleware` allowing developers
to restrict the middleware to a list of paths or regular expresions.

Modify `app.middlewareFromConfig` to pass `config.paths` as the second
arg of `app.middleware`.

Examples:

    // A string path (interpreted via path-to-regexp)
    app.middleware('auth', '/admin', ldapAuth);

    // A regular expression
    app.middleware('initial', /^\/~(admin|root)/, rejectWith404);

    // A list of scopes
    app.middleware('routes', ['/api', /^\/assets/.*\.json$/], foo);

    // From config
    app.middlewareFromConfig(
      handlerFactory,
      {
        phase: 'initial',
        paths: ['/scope', /^\/(a|b)/]
      });
2014-11-19 15:42:54 +01:00
Raymond Feng bd12335542 Merge pull request #814 from strongloop/feature/fix-issue-811
Fix the model name for hasMany/through relation
2014-11-18 10:26:54 -08:00
Miroslav Bajtoš 7581ccf260 Merge pull request #796 from strongloop/feature/cleanup-middleware-config-opts
Cleanup middleware config opts
2014-11-18 19:10:07 +01:00
Raymond Feng 4c7c8901ff Fix the model name for hasMany/through relation 2014-11-17 09:44:20 -08:00
Alex Voitau 6f1b5f61ed Minor: update jsdoc for PersistedModel.updateAll 2014-11-14 20:06:08 -08:00
Miroslav Bajtoš 330292c7f2 server-app: improve jsdoc comments 2014-11-14 09:52:59 +01:00
Miroslav Bajtoš 7647339675 server-app: middleware API improvements
- Rename `config.config` to `config.params`
 - Modify methods to return `this` (fluent API)
2014-11-14 09:52:26 +01:00
Miroslav Bajtoš 22827b6538 Merge pull request #790 from strongloop/feature/merge-phases
Simplify `app.defineMiddlewarePhases`
2014-11-13 09:09:28 +01:00
Miroslav Bajtoš 7fc66a182e Move middleware sources to `server/middleware`
The new location allows developer to use the following identifiers
when loading the middleware using the new declarative style:

    app.middlewareFromConfig(
      require('loopback/server/middleware/rest'),
      { phase: 'routes' });

    app.middlewareFromConfig(
      require('loopback/server/middleware/url-not-found'),
      { phase: 'final' });
2014-11-12 12:44:34 +01:00
Miroslav Bajtoš ae7d99682b Simplify `app.defineMiddlewarePhases`
Refactor the implementation to use the new method `phaseList.zipMerge`.

This is commit is changing the behaviour in the case when
the first new phase does not exist in the current list.

Before the change, all new phases were added just before the "routes"
phase.

After this change, new phases are added to the head of the list,
until an existing phase is encountered, at which point the regular
merge algorithm kicks in.

Example:

    app.defineMiddlewarePhases(['first', 'routes', 'subapps']);

Before the change: code throws an error - 'routes' already exists.

After the change: phases are merged with the following result:

    'first', 'initial', ..., 'routes', 'subapps', ...
2014-11-12 08:59:56 +01:00
Miroslav Bajtoš 4474f8b029 Merge pull request #786 from strongloop/feature/define-middleware-phases
Implement `app.defineMiddlewarePhases`
2014-11-12 08:16:49 +01:00
Raymond Feng f803ecec55 Make sure loopback has all properties from express 2014-11-11 11:27:39 -08:00
Miroslav Bajtoš 98d439050a Implement `app.defineMiddlewarePhases`
Implement method for registering (new) middleware phases.

 - If all names are new, then the phases are added just before
   the "routes" phase.

  - Otherwise the provided list of names is merged with the existing
   phases in such way that the order of phases is preserved.

Example

    // built-in phases:
    // initial, session, auth, parse, routes, files, final

    app.defineMiddlewarePhases('custom');
    // new list of phases
    //   initial, session, auth, parse,
    //   custom,
    //   routes, files, final

    app.defineMiddlewarePhases([
      'initial', 'postinit', 'preauth', 'routes', 'subapps'
    ]);
    // new list of phases
    //   initial,
    //   postinit, preauth,
    //   session, auth, parse, custom,
    //   routes,
    //   subapps,
    //   files, final
2014-11-11 19:45:37 +01:00
Miroslav Bajtoš beb55ee9f4 Merge pull request #787 from strongloop/feature/app-middleware-v2
Implement app.middlewareFromConfig
2014-11-11 19:41:46 +01:00
Miroslav Bajtoš 5578d59631 Implement app.middlewareFromConfig
Implement a function registering a middleware using a factory function
and a JSON config.

Example:

    app.middlewareFromConfig(compression, {
      enabled: true,
      phase: 'initial',
      config: {
        threshold: 128
      }
    });
2014-11-11 18:00:19 +01:00
Miroslav Bajtoš 038c6a454e middleware/token: store the token in current ctx 2014-11-11 11:04:41 +01:00
Miroslav Bajtoš 8f5aea3e3b Fix `loopback.getCurrentContext`
- ensure the method is always defined

 - return `null` when the context is not active
   (we are not inside a request-handling chain)
2014-11-11 11:04:41 +01:00
Miroslav Bajtoš 4e1433b519 Middleware phases - initial implementation
Modify the app and router implementation, so that the middleware is
executed in order defined by phases.

Predefined phases:

    'initial', 'session', 'auth', 'parse', 'routes', 'files', 'final'

Methods defined via `app.use`, `app.route` and friends are executed
as the first thing in 'routes' phase.

API usage:

    app.middleware('initial', compression());
    app.middleware('initial:before', serveFavicon());
    app.middleware('files:after', loopback.urlNotFound());
    app.middleware('final:after', errorHandler());

Middleware flavours:

    // regular handler
    function handler(req, res, next) {
      // do stuff
      next();
    }

    // error handler
    function errorHandler(err, req, res, next) {
      // handle error and/or call next
      next(err);
    }
2014-11-10 19:50:58 +01:00
Raymond Feng 747da886c9 Merge pull request #745 from strongloop/feature/allow-acls-settings-config
Allows ACLs/settings in model config
2014-11-07 11:15:08 -08:00
Raymond Feng 586ea35071 Allows ACLs/settings in model config 2014-11-07 11:14:40 -08:00
crandmck 3cdbd274d2 Add API doc for context middleware - see #337 2014-11-06 14:07:34 -08:00
Rand McKinney 21b28446bf Update persisted-model.js
Note the PersistedMode.create() can take an array of instances, per #710.
2014-11-06 10:56:37 -08:00
Miroslav Bajtoš 4fdcbd16af rest middleware: clean up context config
Modify `loopback.rest()` to read the configuration for
`loopback.context` from `app.get('remoting')`, which is the approach
used for all other configuration options related to the REST transport.
2014-11-05 09:13:45 +01:00
Raymond Feng 885f4e047d Enable the context middleware from loopback.rest 2014-11-05 09:13:45 +01:00
Raymond Feng 246f38c05d Add context propagation middleware
- Implement the middleware `loopback.context`
 - Inject context into juggler and strong-remoting
 - Make http context optional and default to false
 - Optionally mount context middleware from `loopback.rest`
2014-11-05 09:13:44 +01:00
Rand McKinney 0e35c1877c Changes to JSdoc comments
Moved faviconFile to class properties.
2014-11-04 10:59:38 -08:00
Miroslav Bajtoš 48d4ed28d3 Coding style cleanup (Gruntfile, lib)
- Gruntfile: add `jshint` and `jscs` as deps of `grunt test`
 - Gruntfile: temporarily disable checks of `test` scripts
 - .jscsrc: relax jsdoc validation
 - .jshintrc: relax the rule for property access via dot notation
 - lib: fix remaining style issues
2014-11-04 08:25:35 +01:00
Rob Halff 33096dafa7 Enable jscs for `lib`, fix style violations 2014-11-04 08:25:33 +01:00
Rand McKinney 3d201028e1 Remove doc for debug function 2014-11-03 16:26:33 -08:00
Rand McKinney c388696d3f Update registry.js
Add missing quotes to examples in JSdoc.
2014-11-03 16:02:48 -08:00
Miroslav Bajtoš f8c5fc8f6a Merge pull request #726 from strongloop/feature/apidocs-for-loopback.static
Add API docs for `loopback.static`.
2014-11-03 18:33:41 +01:00
Miroslav Bajtoš edd464aca5 Expose path to the built-in favicon file
The path is available via `loopback.faviconFile`.
2014-11-03 10:00:24 +01:00
Miroslav Bajtoš 1c083f1030 Add API docs for `loopback.static`. 2014-11-03 09:20:54 +01:00
Miroslav Bajtoš 292c7ad497 Revert "rest handler options" 2014-10-31 10:06:57 +01:00
Guilherme Cirne ba6bf3f41b REST handler options. 2014-10-30 16:58:30 -02:00
Raymond Feng e0ed755ed3 Make sure GET /:id/exists returns 200 {exists: true|false}
https://github.com/strongloop/loopback/issues/679
2014-10-22 14:39:39 -07:00
Rob Halff b54c70c348 Use === to compare with 0 2014-10-22 18:47:53 +02:00
Rob Halff 84b4fea666 use singlequotes 2014-10-22 18:47:53 +02:00
Rob Halff 4ebf10ae0d add missing semicolons 2014-10-22 18:47:52 +02:00
Fabien Franzen 568c8662b4 Support per-model and per-handler remoting options
Allow the developer to pass custom `remoting` options via Model
settings, e.g.

    PersistedModel.extend(
      'MyModel',
      { name: String },
      {
        remoting: { normalizeHttpPath: true }
      });

Also add `options` arg to `app.handler`, this object is passed directly
to strong-remoting handler.
2014-10-22 09:54:15 +02:00
Miroslav Bajtoš 80020eb273 lib/application: improve URL building algo
When running on Unix and no hostname is specified, use `0.0.0.0`
as the hostname instead of `localhost`.

When running on Windows and the hostname is either not specified or
it is `0.0.0.0` or `::`, use `localhost` in the URL. The reason is
that Windows cannot open URLs using `0.0.0.0` as a hostname.
2014-10-20 13:47:24 +02:00