├── .gitignore ├── orientjs.opts ├── ci ├── README.md ├── orientdb-server-log.properties ├── initialize-ci.sh ├── odb-shared.sh └── orientdb-server-config-1.7.xml ├── test ├── test-connection.json ├── unit │ ├── README.md │ ├── fixtures │ │ ├── profile.model.js │ │ ├── recipeContent.model.js │ │ ├── commentParent.model.js │ │ ├── commentRecipe.model.js │ │ ├── authoredComment.model.js │ │ └── comment.model.js │ ├── query.test.js │ ├── record.test.js │ ├── utils.extend.test.js │ ├── collection.test.js │ ├── utils.rewriteIds.test.js │ └── associations.test.js ├── integration-orientdb │ ├── README.md │ ├── bugs │ │ ├── 40-object_instead_id │ │ │ ├── 40.subprofile.fixture.js │ │ │ ├── 40.profile.fixture.js │ │ │ ├── 40.profileconnection.fixture.js │ │ │ └── 40-object_instead_id.js │ │ ├── 43-orientdb_requestError │ │ │ ├── image.fixture.js │ │ │ ├── user.fixture.js │ │ │ ├── post.fixture.js │ │ │ └── 43-orientdb_requestError.js │ │ ├── 90-no_collections.test.js │ │ ├── 105-delete_customPK.js │ │ ├── 06-find_create.js │ │ ├── 131-undefined_query_parameters.test.js │ │ ├── 44-email_attribute.js │ │ ├── 116-test_edge_creation.js │ │ └── 47-schema_with_id.js │ ├── fixtures │ │ ├── counter.fixture.js │ │ ├── hasManyThrough.stadium.fixture.js │ │ ├── define.schemalessProperties.fixture.js │ │ ├── manyToMany.driverHack.fixture.js │ │ ├── hasManyThrough.friend.fixture.js │ │ ├── hasManyThrough.follows.fixture.js │ │ ├── hasManyThrough.owns.fixture.js │ │ ├── define.properties.fixture.js │ │ ├── hasManyThrough.venueHack.fixture.js │ │ └── define.indexes.fixture.js │ ├── tests │ │ ├── performance │ │ │ ├── fixtures │ │ │ │ ├── manyToMany.taxi.fixture.js │ │ │ │ └── hasManyThrough.team.fixture.js │ │ │ ├── define.test.js │ │ │ └── drop.test.js │ │ ├── adapterCustomMethods │ │ │ ├── removeCircularReferences.js │ │ │ ├── runFunction.test.js │ │ │ ├── getDB_Server.js │ │ │ ├── increment.test.js │ │ │ ├── createEdge.js │ │ │ ├── query.js │ │ │ ├── deleteEdges.js │ │ │ └── decodeURIComponent.test.js │ │ ├── define │ │ │ ├── index.invalid.test.js │ │ │ ├── indexes.js │ │ │ ├── properties.js │ │ │ └── schemalessProperties.js │ │ ├── associations │ │ │ ├── manyThrough.destroy.js │ │ │ ├── manyThrough.update.js │ │ │ ├── manyToMany.joinTableName.find.js │ │ │ ├── manyThroughSelf.add.js │ │ │ ├── manyToMany.selfReferencing.js │ │ │ └── manyThrough.add.js │ │ └── config │ │ │ ├── database.test.js │ │ │ └── options.test..js │ ├── index.js │ └── bootstrap.js └── integration │ └── runner.js ├── .travis.yml ├── .settings └── mocha.js ├── lib ├── collection │ ├── index.js │ └── vertex.js ├── record.js └── query.js ├── LICENSE.md ├── example ├── sails-config │ ├── models.js │ └── connections.js ├── raw │ └── bootstrap.js ├── associations │ └── many-to-many.js └── express │ └── express-example.js ├── package.json ├── .vscode └── launch.json ├── Makefile ├── CONTRIBUTING.md ├── FAQ.md └── .jshintrc /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /orientjs.opts: -------------------------------------------------------------------------------- 1 | --server=localhost 2 | --port=2424 3 | --user=root 4 | --password=root -------------------------------------------------------------------------------- /ci/README.md: -------------------------------------------------------------------------------- 1 | # Continuous Integration 2 | 3 | This folder contains the scripts and configuration files used for [travis-ci](http://travis-ci.com/). -------------------------------------------------------------------------------- /test/test-connection.json: -------------------------------------------------------------------------------- 1 | { 2 | "host": "localhost", 3 | "port": 2424, 4 | "user": "root", 5 | "password": "root", 6 | "options": { 7 | "storage": "plocal" 8 | } 9 | } -------------------------------------------------------------------------------- /test/unit/README.md: -------------------------------------------------------------------------------- 1 | # Adapter Unit Tests 2 | 3 | `waterline-adapter-tests` provide a good layer of basic coverage for adapters. Since usage is standarized, tests are highly reusable. 4 | 5 | That said, if there is adapter-specific logic that you feel should be unit tested, this is the place to do it. 6 | -------------------------------------------------------------------------------- /test/unit/fixtures/profile.model.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | 5 | identity : 'profile', 6 | 7 | attributes : { 8 | displayName : 'string', 9 | comments : { 10 | collection : 'comment', 11 | through : 'authored_comment', 12 | via : 'profileRef', 13 | dominant : true 14 | } 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /test/integration-orientdb/README.md: -------------------------------------------------------------------------------- 1 | # sails-orientdb specific Integration Tests 2 | 3 | `waterline-adapter-tests` provide a good layer of basic coverage for adapters. Since usage is standarized, tests are highly reusable. 4 | 5 | That said, if there are adapter-specific integration tests (e.g. requiring a database connection) that you feel should be added, the sub-folder tests is the place to do it. 6 | -------------------------------------------------------------------------------- /test/integration-orientdb/bugs/40-object_instead_id/40.subprofile.fixture.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | 4 | identity: 'subprofile', 5 | schema: false, 6 | 7 | attributes: { 8 | '*': '', // little hack to get all fields because no schema¬ 9 | profiles: { 10 | collection: 'profile40', 11 | through: 'profileconnection', 12 | via: 'subprofile', 13 | } 14 | } 15 | 16 | }; 17 | -------------------------------------------------------------------------------- /test/unit/fixtures/recipeContent.model.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | 5 | identity : 'recipe_content', 6 | 7 | attributes : { 8 | name : { 9 | type : 'string', 10 | required : true 11 | }, 12 | description : 'text', 13 | comments : { 14 | collection : 'comment', 15 | through : 'comment_recipe', 16 | via : 'recipeRef' 17 | } 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /test/integration-orientdb/bugs/40-object_instead_id/40.profile.fixture.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | 4 | identity: 'profile40', 5 | schema: false, 6 | 7 | attributes: { 8 | 9 | '*': '', // little hack to get all fields because no schema¬ 10 | profiles: { 11 | collection: 'Subprofile', 12 | through: 'profileconnection', 13 | via: 'profile', 14 | dominant: true 15 | } 16 | 17 | } 18 | 19 | }; 20 | -------------------------------------------------------------------------------- /test/integration-orientdb/fixtures/counter.fixture.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | 4 | identity : 'counter', 5 | connection : 'associations', 6 | 7 | orientdbClass : 'document', 8 | 9 | autoPK: false, 10 | associationFinders: false, 11 | 12 | attributes : { 13 | name: { 14 | type: 'string', 15 | primaryKey: true, 16 | required: true, 17 | unique: true 18 | }, 19 | value: { 20 | type: 'integer', 21 | defaultsTo: 0 22 | } 23 | } 24 | 25 | }; 26 | -------------------------------------------------------------------------------- /test/integration-orientdb/tests/performance/fixtures/manyToMany.taxi.fixture.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | 4 | tableName: 'taxiTable', 5 | identity: 'taxi', 6 | connection: 'associations', 7 | 8 | // migrate: 'drop', 9 | attributes: { 10 | medallion: 'integer', 11 | drivers: { 12 | collection: 'driver', 13 | via: 'taxis' 14 | }, 15 | 16 | toJSON: function() { 17 | var obj = this.toObject(); 18 | delete obj.medallion; 19 | return obj; 20 | } 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /test/integration-orientdb/fixtures/hasManyThrough.stadium.fixture.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | 4 | tableName: 'stadiumTable', 5 | identity: 'stadium', 6 | connection: 'associations', 7 | 8 | attributes: { 9 | name: 'string', 10 | teams: { 11 | collection: 'Team', 12 | through: 'venue', 13 | via: 'stadium' 14 | }, 15 | owners: { 16 | collection: 'friend', 17 | through: 'owns', 18 | via: 'stadiumRef' 19 | }, 20 | sponsor: { 21 | model: 'friend' 22 | } 23 | } 24 | 25 | }; 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' 4 | before_install: 5 | - npm install -g npm@latest 6 | before_script: 7 | - ./ci/initialize-ci.sh $ORIENTDB_VERSION 8 | after_script: 9 | - npm run coverage && cat ./coverage/lcov.info | ./node_modules/.bin/codeclimate 10 | env: 11 | global: 12 | - DEBUG="sails-orientdb:*,-sails-orientdb:*:debug" 13 | matrix: 14 | - ORIENTDB_VERSION=1.7.10 15 | - ORIENTDB_VERSION=2.0.12 16 | addons: 17 | code_climate: 18 | repo_token: 9048159e3344dd9ba3afbc6044c1648f472de839baf7cc9eb73089b71439052b 19 | -------------------------------------------------------------------------------- /test/unit/fixtures/commentParent.model.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | 5 | identity : 'comment_parent', 6 | 7 | attributes : { 8 | childRef : { 9 | type : 'string', 10 | foreignKey : true, 11 | references : 'comment', 12 | on : 'id', 13 | onKey : 'id', 14 | via : 'parentRef' 15 | }, 16 | parentRef : { 17 | type : 'string', 18 | foreignKey : true, 19 | references : 'comment', 20 | on : 'id', 21 | onKey : 'id', 22 | via : 'childRef' 23 | } 24 | } 25 | 26 | }; 27 | -------------------------------------------------------------------------------- /test/unit/fixtures/commentRecipe.model.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | 5 | identity : 'comment_recipe', 6 | 7 | attributes : { 8 | recipeRef : { 9 | type : 'string', 10 | foreignKey : true, 11 | references : 'recipe_content', 12 | on : 'id', 13 | onKey : 'id', 14 | via : 'commentRef' 15 | }, 16 | commentRef : { 17 | type : 'string', 18 | foreignKey : true, 19 | references : 'comment', 20 | on : 'id', 21 | onKey : 'id', 22 | via : 'recipeRef' 23 | } 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /.settings/mocha.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Dependencies 4 | var Mocha = require('mocha'); 5 | 6 | // Determine which tests to run based on argument passed to runner 7 | var args = process.argv.splice(2); 8 | var files; 9 | 10 | 11 | 12 | //Define Mocha 13 | var mocha = new Mocha({ 14 | timeout: 60000, 15 | reporter: 'spec', 16 | globals: ['Associations', 'CREATE_TEST_WATERLINE', 'DELETE_TEST_WATERLINE'] 17 | }); 18 | 19 | args.forEach(mocha.addFile.bind(mocha)); 20 | 21 | //Run unit tests 22 | mocha.run(function (failures) { 23 | process.exit(failures); 24 | }); 25 | -------------------------------------------------------------------------------- /ci/orientdb-server-log.properties: -------------------------------------------------------------------------------- 1 | # Specify the handlers to create in the root logger 2 | # (all loggers are children of the root logger) 3 | # The following creates two handlers 4 | handlers = java.util.logging.ConsoleHandler 5 | 6 | # Set the default logging level for the root logger 7 | .level = ALL 8 | 9 | # Set the default logging level for new ConsoleHandler instances 10 | java.util.logging.ConsoleHandler.level = OFF 11 | # Set the default formatter for new ConsoleHandler instances 12 | java.util.logging.ConsoleHandler.formatter = com.orientechnologies.common.log.OLogFormatter 13 | -------------------------------------------------------------------------------- /test/integration-orientdb/fixtures/define.schemalessProperties.fixture.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | 4 | tableName : 'schemalessPropertiesTable', 5 | identity : 'schemaless_properties', 6 | connection : 'associations', 7 | 8 | schema: false, 9 | 10 | attributes : { 11 | schemaProp : 'string', 12 | customColumnProp : { 13 | type: 'string', 14 | columnName: 'customCol' 15 | }, 16 | modelProp : { 17 | model : 'indexes' 18 | }, 19 | collectionProp : { 20 | collection : 'indexes', 21 | via : 'props' 22 | } 23 | } 24 | 25 | }; 26 | -------------------------------------------------------------------------------- /test/integration-orientdb/fixtures/manyToMany.driverHack.fixture.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | 4 | tableName: 'driverTable', 5 | identity: 'driver', 6 | connection: 'associations', 7 | joinTableNames: { 8 | taxis: 'drives' 9 | }, 10 | 11 | // migrate: 'drop', 12 | attributes: { 13 | name: 'string', 14 | taxis: { 15 | collection: 'taxi', 16 | via: 'drivers', 17 | //joinTableName: 'drives', 18 | dominant: true 19 | }, 20 | 21 | toJSON: function() { 22 | var obj = this.toObject(); 23 | delete obj.name; 24 | return obj; 25 | } 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /test/unit/fixtures/authoredComment.model.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | 5 | identity : 'authored_comment', 6 | 7 | attributes : { 8 | profileRef : { 9 | columnName : 'profileRef', 10 | type : 'string', 11 | foreignKey : true, 12 | references : 'profile', 13 | on : 'id', 14 | onKey : 'id', 15 | via : 'commentRef' 16 | }, 17 | commentRef : { 18 | columnName : 'commentRef', 19 | type : 'string', 20 | foreignKey : true, 21 | references : 'comment', 22 | on : 'id', 23 | onKey : 'id', 24 | via : 'profileRef' 25 | }, 26 | }, 27 | }; 28 | -------------------------------------------------------------------------------- /test/integration-orientdb/fixtures/hasManyThrough.friend.fixture.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | 4 | tableName: 'friendTable', 5 | identity: 'friend', 6 | connection: 'associations', 7 | 8 | attributes: { 9 | name: 'string', 10 | followees: { 11 | collection: 'friend', 12 | through: 'follows', 13 | via: 'friend', 14 | dominant: true 15 | }, 16 | followers: { 17 | collection: 'friend', 18 | through: 'follows', 19 | via: 'followee' 20 | }, 21 | stadiums: { 22 | collection: 'stadium', 23 | through: 'owns', 24 | via: 'friendRef', 25 | dominant: true 26 | } 27 | } 28 | 29 | }; 30 | -------------------------------------------------------------------------------- /test/integration-orientdb/bugs/43-orientdb_requestError/image.fixture.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | identity: 'image', 4 | 5 | attributes: { 6 | name: { 7 | type: 'string' 8 | }, 9 | file: { 10 | type: 'json', 11 | isFile: true 12 | }, 13 | footer: { 14 | type: 'string' 15 | }, 16 | // author: { 17 | // model: 'author' 18 | // }, 19 | area: { 20 | type: 'string' 21 | }, 22 | isCrop: { 23 | type: 'boolean' 24 | }, 25 | parent: { 26 | model: 'image' 27 | }, 28 | crops: { 29 | collection: 'image', 30 | via: 'parent' 31 | } 32 | } 33 | }; -------------------------------------------------------------------------------- /test/integration-orientdb/fixtures/hasManyThrough.follows.fixture.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | 4 | tableName: 'followsTable', 5 | identity: 'follows', 6 | connection: 'associations', 7 | 8 | attributes: { 9 | friend: { 10 | columnName: 'friend', 11 | type: 'string', 12 | foreignKey: true, 13 | references: 'friend', 14 | on: 'id', 15 | onKey: 'id', 16 | via: 'followee' 17 | }, 18 | followee: { 19 | columnName: 'followee', 20 | type: 'string', 21 | foreignKey: true, 22 | references: 'friend', 23 | on: 'id', 24 | onKey: 'id', 25 | via: 'friend' 26 | } 27 | } 28 | 29 | }; 30 | -------------------------------------------------------------------------------- /test/integration-orientdb/fixtures/hasManyThrough.owns.fixture.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | 4 | tableName: 'ownsTable', 5 | identity: 'owns', 6 | connection: 'associations', 7 | 8 | attributes: { 9 | friendRef: { 10 | columnName: 'friendRef', 11 | type: 'string', 12 | foreignKey: true, 13 | references: 'friend', 14 | on: 'id', 15 | onKey: 'id', 16 | via: 'stadiumRef' 17 | }, 18 | stadiumRef: { 19 | columnName: 'stadiumRef', 20 | type: 'string', 21 | foreignKey: true, 22 | references: 'stadium', 23 | on: 'id', 24 | onKey: 'id', 25 | via: 'friendRef' 26 | } 27 | } 28 | 29 | }; 30 | -------------------------------------------------------------------------------- /test/integration-orientdb/bugs/40-object_instead_id/40.profileconnection.fixture.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | 4 | identity: 'profileconnection', 5 | schema: false, 6 | 7 | attributes: { 8 | 9 | profileRef: { 10 | columnName: 'profileRef', 11 | type: 'string', 12 | foreignKey: true, 13 | references: 'profile40', 14 | on: 'id', 15 | onKey: 'id', 16 | via: 'subprofileRef' 17 | }, 18 | subprofileRef: { 19 | columnName: 'subprofileRef', 20 | type: 'string', 21 | foreignKey: true, 22 | references: 'subprofile', 23 | on: 'id', 24 | onKey: 'id', 25 | via: 'profileRef' 26 | } 27 | } 28 | 29 | }; 30 | -------------------------------------------------------------------------------- /test/integration-orientdb/fixtures/define.properties.fixture.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | 4 | tableName : 'propertiesTable', 5 | identity : 'properties', 6 | connection : 'associations', 7 | 8 | attributes : { 9 | stringProp : { 10 | type : 'string' 11 | }, 12 | textProp : 'text', 13 | jsonProp : 'json', 14 | arrayProp : 'array', 15 | floatProp : 'float', 16 | emailProp : 'email', 17 | propRequired : { 18 | type : 'string', 19 | required : true 20 | }, 21 | modelProp : { 22 | model : 'indexes' 23 | }, 24 | collectionProp : { 25 | collection : 'indexes', 26 | via : 'props' 27 | } 28 | } 29 | 30 | }; 31 | -------------------------------------------------------------------------------- /test/integration-orientdb/fixtures/hasManyThrough.venueHack.fixture.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | 4 | tableName: 'venueTable', 5 | identity: 'venue', 6 | connection: 'associations', 7 | 8 | attributes: { 9 | seats: 'integer', 10 | teamRef: { 11 | columnName: 'teamRef', 12 | type: 'string', 13 | foreignKey: true, 14 | references: 'team', 15 | on: 'id', 16 | onKey: 'id', 17 | via: 'stadiumRef' 18 | }, 19 | stadiumRef: { 20 | columnName: 'stadiumRef', 21 | type: 'string', 22 | foreignKey: true, 23 | references: 'stadium', 24 | on: 'id', 25 | onKey: 'id', 26 | via: 'teamRef' 27 | } 28 | } 29 | 30 | }; 31 | -------------------------------------------------------------------------------- /test/integration-orientdb/tests/performance/fixtures/hasManyThrough.team.fixture.js: -------------------------------------------------------------------------------- 1 | 2 | // Nothing was changed from the original fixture in waterline-adapter-tests 3 | // but we need this fixture before extending so we can change the connection 4 | 5 | module.exports = { 6 | 7 | tableName: 'teamTable', 8 | identity: 'team', 9 | connection: 'associations', 10 | 11 | attributes: { 12 | name: 'string', 13 | mascot: 'string', 14 | stadiums: { 15 | collection: 'Stadium', 16 | through: 'venue', 17 | via: 'team' 18 | }, 19 | 20 | toJSON: function() { 21 | var obj = this.toObject(); 22 | delete obj.mascot; 23 | return obj; 24 | } 25 | } 26 | 27 | }; 28 | -------------------------------------------------------------------------------- /test/unit/fixtures/comment.model.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | 5 | identity : 'comment', 6 | 7 | attributes : { 8 | comment : 'string', 9 | author : { 10 | collection : 'profile', 11 | through : 'authored_comment', 12 | via : 'commentRef' 13 | }, 14 | parent : { 15 | collection: 'comment', 16 | through: 'comment_parent', 17 | via: 'childRef', 18 | dominant: true 19 | }, 20 | children : { 21 | collection: 'comment', 22 | through: 'comment_parent', 23 | via: 'parentRef' 24 | }, 25 | recipe : { 26 | collection : 'recipe_content', 27 | through : 'comment_recipe', 28 | via : 'commentRef', 29 | dominant: true 30 | } 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /test/integration-orientdb/bugs/43-orientdb_requestError/user.fixture.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | tableName : 'User', 3 | identity : 'dbuser', 4 | schema : true, 5 | attributes : { 6 | id : { 7 | type : 'string', 8 | primaryKey : true, 9 | columnName : '@rid' 10 | }, 11 | username : { 12 | type : 'string', 13 | // required : true, 14 | unique : true 15 | }, 16 | password : { 17 | type : 'string', 18 | // required : false 19 | }, 20 | token : { 21 | type : 'string' 22 | }, 23 | follows : { 24 | collection : 'dbuser', 25 | via : 'followed', 26 | dominant : true 27 | }, 28 | followed : { 29 | collection : 'dbuser', 30 | via : 'follows' 31 | } 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /test/integration-orientdb/tests/adapterCustomMethods/removeCircularReferences.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | 3 | describe('Adapter Custom Methods', function() { 4 | 5 | describe('removeCircularReferences', function() { 6 | 7 | ///////////////////////////////////////////////////// 8 | // TEST METHODS 9 | //////////////////////////////////////////////////// 10 | 11 | it('should remove circular references', function(done) { 12 | 13 | var collection1 = { id: '#13:1' }; 14 | collection1.circ = collection1; 15 | assert.throws(function() { JSON.stringify(collection1); }); 16 | 17 | var result = Associations.Friend.removeCircularReferences(collection1); 18 | assert.equal(result.circ, '#13:1'); 19 | assert.doesNotThrow(function() { JSON.stringify(result); }); 20 | done(); 21 | }); 22 | 23 | }); 24 | 25 | }); -------------------------------------------------------------------------------- /lib/collection/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var Collection = module.exports = function Collection (definition, connection, databaseClass, collectionsByIdentity) { 4 | if(connection.config.options.databaseType === 'document' || 5 | definition.orientdbClass === '' || 6 | definition.orientdbClass === 'document'){ 7 | return new Collection.Document(definition, connection, databaseClass, collectionsByIdentity); 8 | } 9 | 10 | if(definition.orientdbClass === 'E' || 11 | (definition.junctionTable && definition.orientdbClass !== 'V')){ 12 | return new Collection.Edge(definition, connection, databaseClass, collectionsByIdentity); 13 | } 14 | 15 | return new Collection.Vertex(definition, connection, databaseClass, collectionsByIdentity); 16 | }; 17 | 18 | Collection.Document = require('./document'); 19 | Collection.Vertex = require('./vertex'); 20 | Collection.Edge = require('./edge'); 21 | -------------------------------------------------------------------------------- /test/integration-orientdb/bugs/90-no_collections.test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | 3 | var self = this; 4 | 5 | describe('Bug #90: instantiate waterline without collections', function () { 6 | 7 | describe('instantation', function () { 8 | 9 | ///////////////////////////////////////////////////// 10 | // TEST SETUP 11 | //////////////////////////////////////////////////// 12 | 13 | after(function (done) { 14 | DELETE_TEST_WATERLINE('test_bug_90', done); 15 | }); 16 | 17 | 18 | ///////////////////////////////////////////////////// 19 | // TEST METHODS 20 | //////////////////////////////////////////////////// 21 | 22 | it('should be able to save changes on model returned by create', function (done) { 23 | CREATE_TEST_WATERLINE(self, 'test_bug_90', {}, function (err) { 24 | assert(!err, err); 25 | done(); 26 | }); 27 | }); 28 | 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | -- 3 | 4 | Copyright © 2014 AppsCot 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /test/integration-orientdb/tests/define/index.invalid.test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | 3 | var self = this, 4 | fixtures; 5 | 6 | describe('Define related Operations', function() { 7 | 8 | describe('Indexes', function() { 9 | 10 | ///////////////////////////////////////////////////// 11 | // TEST SETUP 12 | //////////////////////////////////////////////////// 13 | 14 | fixtures = { 15 | IndexFixture : { 16 | identity : 'thing', 17 | attributes : { 18 | name : 'string', 19 | indexFunky : { 20 | type : 'string', 21 | index: 'funky' 22 | } 23 | } 24 | } 25 | }; 26 | 27 | 28 | ///////////////////////////////////////////////////// 29 | // TEST METHODS 30 | //////////////////////////////////////////////////// 31 | 32 | it('should throw error while attempting to create invalid index', function(done) { 33 | CREATE_TEST_WATERLINE(self, 'test_index_invalid', fixtures, function(err){ 34 | assert(err); 35 | assert(err.message.indexOf('Index funky is not supported') === 0); 36 | done(); 37 | }); 38 | }); 39 | 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /test/integration-orientdb/fixtures/define.indexes.fixture.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | 4 | tableName : 'indexesTable', 5 | identity : 'indexes', 6 | connection : 'associations', 7 | 8 | attributes : { 9 | name : 'string', 10 | indexUnique : { 11 | type : 'string', 12 | unique : true 13 | }, 14 | indexNotUnique : { 15 | columnName : 'indexDuplicates', 16 | type : 'string', 17 | index : true 18 | }, 19 | indexFulltext : { 20 | columnName : 'indexFulltext', 21 | type : 'string', 22 | index : 'fulltext' 23 | }, 24 | indexDictionary : { 25 | type : 'string', 26 | index : 'dictionary' 27 | }, 28 | 29 | indexUniqueHash : { 30 | type : 'string', 31 | index : 'unique_hash_index' 32 | }, 33 | indexNotUniqueHash : { 34 | type : 'string', 35 | index : 'notunique_hash_index' 36 | }, 37 | indexFulltextHash : { 38 | type : 'string', 39 | index : 'fulltext_hash_index' 40 | }, 41 | indexDictionaryHash : { 42 | type : 'string', 43 | index : 'dictionary_hash_index' 44 | }, 45 | 46 | props: { 47 | model: 'properties' 48 | }, 49 | 50 | schemalessProps: { 51 | model: 'schemaless_properties' 52 | } 53 | } 54 | 55 | }; 56 | -------------------------------------------------------------------------------- /test/integration-orientdb/bugs/43-orientdb_requestError/post.fixture.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | 4 | identity: 'post', 5 | 6 | attributes: { 7 | title: { 8 | type: 'string' 9 | }, 10 | slug: { 11 | type: 'string' 12 | }, 13 | editorialPriority: { 14 | type: 'string' 15 | }, 16 | sectionPriority: { 17 | type: 'string' 18 | }, 19 | html: { 20 | type: 'string' 21 | }, 22 | editor_html: { 23 | type: 'string' 24 | }, 25 | featureImage: { 26 | model: 'image' 27 | }, 28 | area: { 29 | type: 'string' 30 | }, 31 | excerpt: { 32 | type: 'string' 33 | }, 34 | content: { 35 | type: 'string', 36 | }, 37 | publicationDate: { 38 | type: 'datetime' 39 | }, 40 | // categories:{ 41 | // collection: 'category', 42 | // through: 'post_category', 43 | // via: 'post', 44 | // dominant: true 45 | // }, 46 | // author:{ 47 | // model:'author' 48 | // }, 49 | // status:{ 50 | // model:'postStatus' 51 | // }, 52 | address: { 53 | type: 'string' 54 | }, 55 | addressReference: { 56 | type: 'string' 57 | }, 58 | latitude: { 59 | type: 'float' 60 | }, 61 | longitude: { 62 | type: 'float' 63 | } 64 | } 65 | }; -------------------------------------------------------------------------------- /ci/initialize-ci.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PARENT_DIR=$(dirname $(cd "$(dirname "$0")"; pwd)) 4 | CI_DIR="$PARENT_DIR/ci/environment" 5 | 6 | ODB_VERSION=${1:-"1.7-rc2"} 7 | ODB_DIR="${CI_DIR}/orientdb-community-${ODB_VERSION}" 8 | ODB_LAUNCHER="${ODB_DIR}/bin/server.sh" 9 | 10 | echo "=== Initializing CI environment ===" 11 | 12 | cd "$PARENT_DIR" 13 | 14 | . "$PARENT_DIR/ci/odb-shared.sh" 15 | 16 | if [ ! -d "$ODB_DIR" ]; then 17 | # Download and extract OrientDB server 18 | echo "--- Downloading OrientDB v${ODB_VERSION} ---" 19 | odb_download_server $ODB_VERSION $CI_DIR 20 | 21 | # Ensure that launcher script is executable and copy configurations file 22 | echo "--- Setting up OrientDB ---" 23 | chmod +x $ODB_LAUNCHER 24 | chmod -R +rw "${ODB_DIR}/config/" 25 | if [[ $ODB_VERSION == *"1.7"* ]]; then 26 | cp $PARENT_DIR/ci/orientdb-server-config-1.7.xml "${ODB_DIR}/config/orientdb-server-config.xml" 27 | else 28 | cp $PARENT_DIR/ci/orientdb-server-config.xml "${ODB_DIR}/config/" 29 | fi 30 | cp $PARENT_DIR/ci/orientdb-server-log.properties "${ODB_DIR}/config/" 31 | else 32 | echo "!!! Found OrientDB v${ODB_VERSION} in ${ODB_DIR} !!!" 33 | fi 34 | 35 | # Start OrientDB in background. 36 | echo "--- Starting an instance of OrientDB ---" 37 | sh -c $ODB_LAUNCHER /dev/null & 38 | 39 | # Wait a bit for OrientDB to finish the initialization phase. 40 | 41 | if [[ $ODB_VERSION == *"1.7"* ]]; then 42 | sleep 15 43 | else 44 | sleep 30 45 | fi 46 | printf "\n=== The CI environment has been initialized ===\n" 47 | -------------------------------------------------------------------------------- /example/sails-config/models.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Default model configuration 3 | * (sails.config.models) 4 | * 5 | * Unless you override them, the following properties will be included 6 | * in each of your models. 7 | * 8 | * For more info on Sails models, see: 9 | * http://sailsjs.org/#/documentation/concepts/ORM 10 | */ 11 | 12 | module.exports.models = { 13 | 14 | /*************************************************************************** 15 | * * 16 | * Your app's default connection. i.e. the name of one of your app's * 17 | * connections (see `config/connections.js`) * 18 | * * 19 | ***************************************************************************/ 20 | connection: 'someOrientdbServer', 21 | 22 | /*************************************************************************** 23 | * * 24 | * How and whether Sails will attempt to automatically rebuild the * 25 | * tables/collections/etc. in your schema. * 26 | * * 27 | * See http://sailsjs.org/#/documentation/concepts/ORM/model-settings.html * 28 | * * 29 | ***************************************************************************/ 30 | // migrate: 'alter' 31 | 32 | }; 33 | -------------------------------------------------------------------------------- /test/integration-orientdb/bugs/105-delete_customPK.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | 3 | var self = this; 4 | 5 | describe('Bug #105: delete fails when using custom PK', function () { 6 | before(function (done) { 7 | 8 | var fixtures = { 9 | UserFixture: { 10 | identity: 'user', 11 | autoPK: false, 12 | 13 | attributes: { 14 | sid: { 15 | type: 'string', 16 | primaryKey: true, 17 | unique: true, 18 | required: true 19 | }, 20 | name: { 21 | type: 'string' 22 | } 23 | } 24 | } 25 | }; 26 | 27 | CREATE_TEST_WATERLINE(self, 'test_bug_105', fixtures, done); 28 | }); 29 | after(function (done) { 30 | DELETE_TEST_WATERLINE('test_bug_105', done); 31 | }); 32 | 33 | describe('delete user', function () { 34 | 35 | ///////////////////////////////////////////////////// 36 | // TEST SETUP 37 | //////////////////////////////////////////////////// 38 | 39 | before(function (done) { 40 | self.collections.User.create({ sid: 'user2', name: 'john' }, done); 41 | }); 42 | 43 | 44 | ///////////////////////////////////////////////////// 45 | // TEST METHODS 46 | //////////////////////////////////////////////////// 47 | 48 | it('should delete user successfully', function (done) { 49 | self.collections.User.destroy({ name: 'john' }, function (err, users) { 50 | if (err) {  return done(err); } 51 | assert(users[0]); 52 | done(); 53 | }); 54 | }); 55 | 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /test/unit/query.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Test dependencies 3 | */ 4 | var assert = require('assert'), 5 | Query = require('../../lib/query'); 6 | 7 | var query = new Query({}, { config: { options: { decodeURIComponent: true } } }); 8 | 9 | describe('associations class', function () { 10 | 11 | var rid1 = '%231%3A4', decodedRid1 = '#1:4', rid2 = '%2322%3A33', decodedRid2 = '#22:33'; 12 | 13 | it('decode: should decode an URI encoded RID string', function(done){ 14 | assert.equal(query.decode(rid1), decodedRid1); 15 | assert.equal(query.decode(rid2), decodedRid2); 16 | 17 | done(); 18 | }); 19 | 20 | it('decode: should decode an URI encoded RID array', function(done){ 21 | assert.deepEqual(query.decode([rid1, rid2]), [decodedRid1, decodedRid2]); 22 | 23 | done(); 24 | }); 25 | 26 | it('decode: should decode an URI encoded RID within criteria modifiers', function(done){ 27 | assert.deepEqual(query.decode({ '!': rid1 }), { '!': decodedRid1 }); 28 | assert.deepEqual(query.decode({ '!': [rid1, rid2] }), { '!': [decodedRid1, decodedRid2] }); 29 | 30 | done(); 31 | }); 32 | 33 | it('decode: non URI encoded fields should pass transparently', function(done){ 34 | var date = new Date(); 35 | assert.equal(query.decode(decodedRid1), decodedRid1); 36 | assert.equal(query.decode(date), date); 37 | assert.deepEqual(query.decode([decodedRid1, decodedRid2]), [decodedRid1, decodedRid2]); 38 | assert.deepEqual(query.decode({ '!': decodedRid1 }), { '!': decodedRid1 }); 39 | assert.deepEqual(query.decode({ '!': [decodedRid1, decodedRid2] }), { '!': [decodedRid1, decodedRid2] }); 40 | 41 | done(); 42 | }); 43 | 44 | }); -------------------------------------------------------------------------------- /test/integration-orientdb/bugs/06-find_create.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | 3 | var self = this; 4 | 5 | describe('Bug #06: Models returned from find() and create() are different', function() { 6 | before(function (done) { 7 | 8 | var fixtures = { 9 | UserFixture : { 10 | identity : 'user', 11 | 12 | attributes : { 13 | name : { 14 | type : 'string' 15 | }, 16 | email : { 17 | type : 'email' 18 | }, 19 | } 20 | } 21 | }; 22 | 23 | CREATE_TEST_WATERLINE(self, 'test_bug_06', fixtures, done); 24 | }); 25 | after(function (done) { 26 | DELETE_TEST_WATERLINE('test_bug_06', done); 27 | }); 28 | 29 | describe('create user', function() { 30 | 31 | ///////////////////////////////////////////////////// 32 | // TEST SETUP 33 | //////////////////////////////////////////////////// 34 | 35 | 36 | ///////////////////////////////////////////////////// 37 | // TEST METHODS 38 | //////////////////////////////////////////////////// 39 | 40 | it('should be able to save changes on model returned by create', function(done) { 41 | self.collections.User.create({ name: 'john', email: 'email@example.com' }, function(err, user) { 42 | if(err) { return done(err); } 43 | assert(user); 44 | 45 | user.name = 'joe'; 46 | user.save(function(err){ 47 | if(err) { return done(err); } 48 | 49 | self.collections.User.findOne(user.id, function(err, usr){ 50 | if(err) { return done(err); } 51 | assert(usr); 52 | assert.equal(usr.email, user.email); 53 | assert.equal(usr.name, 'joe'); 54 | done(); 55 | }); 56 | 57 | }); 58 | }); 59 | }); 60 | 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /test/unit/record.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Test dependencies 3 | */ 4 | var assert = require('assert'), 5 | Record = require('../../lib/record'), 6 | RID = require('orientjs').RID, 7 | _ = require('lodash'), 8 | util = require('util'); 9 | 10 | 11 | describe('record helper class', function () { 12 | 13 | var record; 14 | 15 | before(function(done){ 16 | record = new Record(); 17 | done(); 18 | }); 19 | 20 | it('normalizeId: should correctly normalize an id', function (done) { 21 | var collection1 = { 22 | name: 'no id collection' 23 | }; 24 | var testCollection1 = _.clone(collection1); 25 | record.normalizeId(testCollection1); 26 | assert(_.isEqual(testCollection1, collection1)); 27 | 28 | 29 | var testCollection2 = { 30 | name: 'id collection', 31 | id: '#1:0' 32 | }; 33 | record.normalizeId(testCollection2); 34 | assert(_.isUndefined(testCollection2.id)); 35 | assert(testCollection2['@rid'] instanceof RID); 36 | assert.equal(testCollection2['@rid'].cluster, 1); 37 | assert.equal(testCollection2['@rid'].position, 0); 38 | assert(_.isEqual(testCollection2['@rid'], new RID('#1:0')), 'not equal to RID(\'#1:0\'), actual value: ' + util.inspect(testCollection2['@rid'])); 39 | 40 | var testCollection3 = { 41 | name: 'id collection', 42 | '@rid': new RID('#2:0') 43 | }; 44 | record.normalizeId(testCollection3); 45 | assert(_.isUndefined(testCollection3.id)); 46 | assert(_.isEqual(testCollection3['@rid'], new RID('#2:0'))); 47 | 48 | var testCollection4 = { 49 | name: 'id collection', 50 | id: '#1:0', 51 | '@rid': new RID('#2:0') 52 | }; 53 | record.normalizeId(testCollection4); 54 | assert(_.isUndefined(testCollection4.id)); 55 | assert(_.isEqual(testCollection4['@rid'], new RID('#1:0'))); 56 | 57 | done(); 58 | }); 59 | 60 | 61 | }); 62 | -------------------------------------------------------------------------------- /test/integration-orientdb/tests/performance/define.test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | 3 | var self = this; 4 | 5 | // Require Fixtures 6 | var localFixturesPath = '../../fixtures/'; 7 | 8 | var fixtures = { 9 | TeamFixture: require('./fixtures/hasManyThrough.team.fixture.js'), 10 | StadiumFixture: require(localFixturesPath + 'hasManyThrough.stadium.fixture'), 11 | VenueFixture: require(localFixturesPath + 'hasManyThrough.venueHack.fixture'), 12 | FriendFixture: require(localFixturesPath + 'hasManyThrough.friend.fixture'), 13 | FollowsFixture: require(localFixturesPath + 'hasManyThrough.follows.fixture'), 14 | OwnsFixture: require(localFixturesPath + 'hasManyThrough.owns.fixture'), 15 | IndexesFixture: require(localFixturesPath + 'define.indexes.fixture'), 16 | PropertiesFixture: require(localFixturesPath + 'define.properties.fixture'), 17 | SchemalessPropertiesFixture: require(localFixturesPath + 'define.schemalessProperties.fixture') 18 | }; 19 | 20 | describe('Performance', function() { 21 | 22 | describe('define', function() { 23 | 24 | ///////////////////////////////////////////////////// 25 | // TEST SETUP 26 | //////////////////////////////////////////////////// 27 | 28 | after(function (done) { 29 | DELETE_TEST_WATERLINE('test_performance_define', done); 30 | }); 31 | 32 | 33 | ///////////////////////////////////////////////////// 34 | // TEST METHODS 35 | //////////////////////////////////////////////////// 36 | 37 | it('should define all fixtures in timely manner', function(done) { 38 | this.timeout(3000); 39 | console.time('performance_define'); 40 | CREATE_TEST_WATERLINE(self, 'test_performance_define', fixtures, function(err){ 41 | if(err) { done(err); } 42 | console.timeEnd('performance_define'); 43 | done(); 44 | }); 45 | }); 46 | 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sails-orientdb", 3 | "version": "0.10.60", 4 | "description": "OrientDB adapter for Waterline / Sails.js ORM", 5 | "main": "./lib/adapter.js", 6 | "scripts": { 7 | "pretest": "./node_modules/.bin/jshint ./lib", 8 | "test": "make test", 9 | "coverage": "make coverage", 10 | "lint": "./node_modules/.bin/jshint ./lib" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git@github.com:appscot/sails-orientdb.git" 15 | }, 16 | "keywords": [ 17 | "orientdb", 18 | "orient", 19 | "orm", 20 | "waterline", 21 | "sails", 22 | "sailsjs", 23 | "sails.js", 24 | "graph", 25 | "graphdb" 26 | ], 27 | "author": { 28 | "name": "Dario Marcelino", 29 | "email": "dario@appscot.com" 30 | }, 31 | "contributors": [ 32 | { 33 | "name": "Srinath Janakiraman", 34 | "email": "vjsrinath@live.com" 35 | }, 36 | { 37 | "name": "Gaurav Dhiman", 38 | "email": "gauravd.chd@gmail.com" 39 | } 40 | ], 41 | "license": "MIT", 42 | "readmeFilename": "README.md", 43 | "dependencies": { 44 | "async": "^1.0.0", 45 | "bluebird": "^3.4.1", 46 | "debug-logger": "^0.4.0", 47 | "lodash": "^3.10.1", 48 | "orientjs": "^2.2.1", 49 | "waterline-criteria": "^1.0.1", 50 | "waterline-cursor": "~0.0.5", 51 | "waterline-sequel-orientdb": "^0.1.0" 52 | }, 53 | "devDependencies": { 54 | "codeclimate-test-reporter": "~0.3.3", 55 | "istanbul": "^0.4.4", 56 | "jshint": "*", 57 | "mocha": "*", 58 | "waterline-schema": "0.1.18", 59 | "waterline": "0.10.27", 60 | "waterline-adapter-tests": "0.10.11" 61 | }, 62 | "waterlineAdapter": { 63 | "type": "orientdb", 64 | "interfaces": [ 65 | "semantic", 66 | "queryable", 67 | "associations", 68 | "migratable", 69 | "sql" 70 | ], 71 | "waterlineVersion": "~0.10.18" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /test/integration-orientdb/tests/adapterCustomMethods/runFunction.test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | 3 | describe('Adapter Custom Methods', function() { 4 | 5 | describe('runFunction', function() { 6 | 7 | ///////////////////////////////////////////////////// 8 | // TEST SETUP 9 | //////////////////////////////////////////////////// 10 | 11 | before(function(done) { 12 | Associations.Friend.getDB(function(db) { 13 | db.createFn('runme1', function(str, str2) { 14 | str = str || ''; 15 | str2 = str2 || ''; 16 | return 'this ' + str + ' work' + str2; 17 | }) 18 | .then(function() { done(); }) 19 | .catch(done); 20 | }); 21 | }); 22 | 23 | ///////////////////////////////////////////////////// 24 | // TEST METHODS 25 | //////////////////////////////////////////////////// 26 | 27 | it('should run a server function with 1 argument', function(done) { 28 | Associations.Friend.runFunction('runme1', 'does').from('OUser').limit(1).one() 29 | .then(function(res) { 30 | assert.equal(res.runme1, "this does work"); 31 | done(); 32 | }) 33 | .catch(done); 34 | }); 35 | 36 | it('should run a server function without params', function(done) { 37 | Associations.Friend.runFunction('runme1').from('OUser').limit(1).one() 38 | .then(function(res) { 39 | assert.equal(res.runme1, "this work"); 40 | done(); 41 | }) 42 | .catch(done); 43 | }); 44 | 45 | it('should run a server function with multiple arguments', function(done) { 46 | Associations.Friend.runFunction('runme1', 'does', '!').from('OUser').limit(1).one() 47 | .then(function(res) { 48 | assert.equal(res.runme1, "this does work!"); 49 | done(); 50 | }) 51 | .catch(done); 52 | }); 53 | 54 | }); 55 | 56 | }); -------------------------------------------------------------------------------- /ci/odb-shared.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | odb_compare_version () { 4 | # TODO: this function does not handle well versions with additional rank 5 | # indicators such as "-rc1" but I guess it suffices for now. 6 | GTVER=$(echo "$1\n$2" | sort -n --reverse | head -1) 7 | 8 | if [ $1 = $2 ]; then 9 | echo 0 10 | elif [ $1 = $GTVER ]; then 11 | echo 1 12 | else 13 | echo -1 14 | fi 15 | } 16 | 17 | odb_command_exists () { 18 | type "$1" >/dev/null 2>&1 ; 19 | } 20 | 21 | odb_download () { 22 | OUTPUT_DIR="${2:-$(pwd)}" 23 | 24 | if [ ! -d "$OUTPUT_DIR" ]; then 25 | mkdir "$OUTPUT_DIR" 26 | fi 27 | 28 | if odb_command_exists "wget" ; then 29 | wget -q -O "$OUTPUT_DIR/$ODB_C_PACKAGE" "$1" 30 | elif odb_command_exists "curl" ; then 31 | ( cd "$OUTPUT_DIR" > /dev/null ; curl --silent -O "$1" ; ) 32 | else 33 | echo "Cannot download $1 [missing wget or curl]" 34 | exit 1 35 | fi 36 | } 37 | 38 | odb_download_server () { 39 | ODB_VERSION=$1 40 | CI_DIR=$2 41 | 42 | ODB_PACKAGE="orientdb-community-${ODB_VERSION}" 43 | 44 | 45 | ODB_PACKAGE_EXT="zip" 46 | ODB_C_PACKAGE=${ODB_PACKAGE}.${ODB_PACKAGE_EXT} 47 | 48 | OUTPUT_DIR="${2:-$(pwd)}" 49 | 50 | if [ ! -d "$OUTPUT_DIR" ]; then 51 | mkdir "$OUTPUT_DIR" 52 | fi 53 | 54 | if odb_command_exists "mvn" ; then 55 | mvn org.apache.maven.plugins:maven-dependency-plugin:2.8:get -Dartifact=com.orientechnologies:orientdb-community:$ODB_VERSION:$ODB_PACKAGE_EXT:distribution -DremoteRepositories="https://oss.sonatype.org/content/repositories/snapshots/,https://oss.sonatype.org/content/repositories/releases/" -Ddest=$OUTPUT_DIR/$ODB_C_PACKAGE 56 | else 57 | echo "Cannot download $1 [maven is not installed]" 58 | exit 1 59 | fi 60 | 61 | ODB_PACKAGE_PATH="${CI_DIR}/${ODB_PACKAGE}.${ODB_PACKAGE_EXT}" 62 | 63 | if [ $ODB_PACKAGE_EXT = "zip" ]; then 64 | unzip -q $ODB_PACKAGE_PATH -d ${CI_DIR} 65 | elif [ $ODB_PACKAGE_EXT = "tar.gz" ]; then 66 | tar xf $ODB_PACKAGE_PATH -C $CI_DIR 67 | fi; 68 | } 69 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.0", 3 | // List of configurations. Add new configurations or edit existing ones. 4 | // ONLY "node" and "mono" are supported, change "type" to switch. 5 | "configurations": [ 6 | { 7 | // Name of configuration; appears in the launch configuration drop down menu. 8 | "name": "Integration tests", 9 | // Type of configuration. Possible values: "node", "mono". 10 | "type": "node", 11 | // Workspace relative or absolute path to the program. 12 | "program": "test/integration/runner.js", 13 | // Automatically stop program after launch. 14 | "stopOnEntry": true, 15 | // Command line arguments passed to the program. 16 | "args": [], 17 | // Workspace relative or absolute path to the working directory of the program being debugged. Default is the current workspace. 18 | "cwd": ".", 19 | // Workspace relative or absolute path to the runtime executable to be used. Default is the runtime executable on the PATH. 20 | "runtimeExecutable": null, 21 | // Environment variables passed to the program. 22 | "env": { } 23 | }, 24 | { 25 | "name": "Unit tests", 26 | "type": "node", 27 | "program": ".settings/mocha.js", 28 | "stopOnEntry": true, 29 | "args": ["test/unit/*.js", "test/unit/**/*.js"], 30 | "cwd": ".", 31 | "runtimeExecutable": null, 32 | "env": { } 33 | }, 34 | { 35 | "name": "OrientDB Integration tests", 36 | "type": "node", 37 | "program": ".settings/mocha.js", 38 | "stopOnEntry": true, 39 | "args": [ 40 | "test/integration-orientdb/*.js", 41 | "test/integration-orientdb/tests/**/*.js", 42 | "test/integration-orientdb/bugs/*.js", 43 | "test/integration-orientdb/bugs/**/*.js" 44 | ], 45 | "cwd": ".", 46 | "runtimeExecutable": null, 47 | "env": { } 48 | }, 49 | { 50 | "name": "Attach", 51 | "type": "node", 52 | // TCP/IP address. Default is "localhost". 53 | "address": "localhost", 54 | // Port to attach to. 55 | "port": 5858 56 | } 57 | ] 58 | } 59 | -------------------------------------------------------------------------------- /example/sails-config/connections.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Connections 3 | * (sails.config.connections) 4 | * 5 | * `Connections` are like "saved settings" for your adapters. What's the difference between 6 | * a connection and an adapter, you might ask? An adapter (e.g. `sails-mysql`) is generic-- 7 | * it needs some additional information to work (e.g. your database host, password, user, etc.) 8 | * A `connection` is that additional information. 9 | * 10 | * Each model must have a `connection` property (a string) which is references the name of one 11 | * of these connections. If it doesn't, the default `connection` configured in `config/models.js` 12 | * will be applied. Of course, a connection can (and usually is) shared by multiple models. 13 | * . 14 | * Note: If you're using version control, you should put your passwords/api keys 15 | * in `config/local.js`, environment variables, or use another strategy. 16 | * (this is to prevent you inadvertently sensitive credentials up to your repository.) 17 | * 18 | * For more information on configuration, check out: 19 | * http://sailsjs.org/#/documentation/reference/sails.config/sails.config.connections.html 20 | */ 21 | 22 | module.exports.connections = { 23 | 24 | /*************************************************************************** 25 | * * 26 | * OrientDB is a NoSQL Document and Graph DBMS. * 27 | * http://en.wikipedia.org/wiki/OrientDB * 28 | * * 29 | * Run: npm sails-orientdb * 30 | * * 31 | ***************************************************************************/ 32 | 33 | someOrientdbServer: { 34 | adapter: 'sails-orientdb', 35 | host: 'localhost', 36 | port: 2424, 37 | user: 'YOUR_ORIENTDB_USER', 38 | password: 'YOUR_ORIENTDB_PASSWORD', 39 | database: 'YOUR_ORIENTDB_DB' 40 | } 41 | 42 | }; 43 | -------------------------------------------------------------------------------- /example/raw/bootstrap.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Based on https://github.com/balderdashy/waterline/blob/master/example/raw/bootstrap.js 3 | */ 4 | 5 | /** 6 | * Module dependencies 7 | */ 8 | 9 | var Waterline = require('waterline'), 10 | orientAdapter = require('../../lib/adapter'); 11 | 12 | 13 | /** 14 | * Set up Waterline with the specified 15 | * models, connections, and adapters. 16 | 17 | @param options 18 | :: {Object} adapters [i.e. a dictionary] 19 | :: {Object} connections [i.e. a dictionary] 20 | :: {Object} collections [i.e. a dictionary] 21 | 22 | @param {Function} cb 23 | () {Error} err 24 | () ontology 25 | :: {Object} collections 26 | :: {Object} connections 27 | 28 | @return {Waterline} 29 | */ 30 | 31 | module.exports = function bootstrap( options, cb ) { 32 | 33 | var adapters = options.adapters || { 'default': orientAdapter, 'sails-orientdb': orientAdapter }; 34 | var connections = options.connections || {}; 35 | var collections = options.collections || {}; 36 | 37 | 38 | 39 | for(var key in adapters) { 40 | // Make sure our adapter defs have `identity` properties 41 | adapters[key].identity = adapters[key].identity || key; 42 | } 43 | 44 | 45 | var extendedCollections = []; 46 | for(var key in collections) { 47 | 48 | // Make sure our collection defs have `identity` properties 49 | collections[key].identity = collections[key].identity || key; 50 | 51 | // Fold object of collection definitions into an array 52 | // of extended Waterline collections. 53 | extendedCollections.push(Waterline.Collection.extend(collections[key])); 54 | } 55 | 56 | 57 | // Instantiate Waterline and load the already-extended 58 | // Waterline collections. 59 | var waterline = new Waterline(); 60 | extendedCollections.forEach(function (collection) { 61 | waterline.loadCollection(collection); 62 | }); 63 | 64 | 65 | // Initialize Waterline 66 | // (and tell it about our adapters) 67 | waterline.initialize({ 68 | adapters: adapters, 69 | connections: connections 70 | }, cb); 71 | 72 | return waterline; 73 | }; 74 | -------------------------------------------------------------------------------- /test/integration-orientdb/tests/associations/manyThrough.destroy.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | _ = require('lodash'); 3 | 4 | describe('Association Interface', function() { 5 | 6 | describe('n:m through association :: .destroy()', function() { 7 | 8 | ///////////////////////////////////////////////////// 9 | // TEST SETUP 10 | //////////////////////////////////////////////////// 11 | 12 | var stadiumRecord, teamRecord, edgeRecord; 13 | 14 | before(function(done) { 15 | Associations.Stadium.create({ name: 'hasManyThrough stadium destroy'}, function(err, stadium) { 16 | if(err) { return done(err); } 17 | stadiumRecord = stadium; 18 | 19 | Associations.Team.create({ name: 'hasManyThrough team destroy', mascot: 'elephant' }, function(err, team) { 20 | if(err) { return done(err); } 21 | teamRecord = team; 22 | 23 | stadiumRecord.teams.add(teamRecord.id); 24 | stadiumRecord.save(function(err){ 25 | if(err) { return done(err); } 26 | 27 | Associations.Venue.findOne({ out: stadiumRecord.id }, function(err, edge){ 28 | if(err) { return done(err); } 29 | edgeRecord = edge; 30 | done(); 31 | }); 32 | }); 33 | }); 34 | }); 35 | }); 36 | 37 | describe('with an object', function() { 38 | 39 | ///////////////////////////////////////////////////// 40 | // TEST METHODS 41 | //////////////////////////////////////////////////// 42 | 43 | it('should delete the edge linking stadium and team', function(done) { 44 | Associations.Venue.destroy({ id: edgeRecord.id }, function(err, edges){ 45 | if(err) { return done(err); } 46 | assert.equal(edges[0].id, edgeRecord.id); 47 | 48 | Associations.Venue.find({ id: edgeRecord.id }, function(err, edges){ 49 | if(err) { return done(err); } 50 | assert.equal(edges.length, 0); 51 | done(); 52 | }); 53 | 54 | }); 55 | }); 56 | 57 | }); 58 | 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /test/integration-orientdb/bugs/131-undefined_query_parameters.test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | 3 | var self = this; 4 | 5 | describe('Bug #131: Undefined query parameters generated when populating optional one-way associations', function () { 6 | before(function (done) { 7 | 8 | var fixtures = { 9 | FileFixture: { 10 | identity: 'file', 11 | 12 | attributes: { 13 | name: { 14 | type : 'string', 15 | required : true 16 | } 17 | } 18 | }, 19 | 20 | PersonFixture: { 21 | identity: 'person', 22 | 23 | attributes: { 24 | name: { 25 | type : 'string', 26 | required : true 27 | }, 28 | avatar: { 29 | model: 'file' 30 | } 31 | } 32 | } 33 | }; 34 | 35 | CREATE_TEST_WATERLINE(self, 'test_bug_131', fixtures, done); 36 | }); 37 | 38 | after(function (done) { 39 | DELETE_TEST_WATERLINE('test_bug_131', done); 40 | }); 41 | 42 | describe('... finding and populating a person created without an avatar', function () { 43 | 44 | ///////////////////////////////////////////////////// 45 | // TEST SETUP 46 | //////////////////////////////////////////////////// 47 | 48 | before(function (done) { 49 | self.collections.Person.create({ name: 'mike' }, done); 50 | }); 51 | 52 | 53 | ///////////////////////////////////////////////////// 54 | // TEST METHODS 55 | //////////////////////////////////////////////////// 56 | 57 | it('should not try to send undefined parameters to the database', function (done) { 58 | self.collections.Person.getDB().on('beginQuery', function (query) { 59 | var params = query && query.params && query.params.params ? 60 | query.params.params : undefined; 61 | 62 | if (params) { 63 | for (var param in params) { 64 | if (params.hasOwnProperty(param)) { 65 | assert(params[param] !== undefined); 66 | } 67 | } 68 | } 69 | }); 70 | 71 | self.collections.Person.find({ name: 'mike' }).populate('avatar').exec(done); 72 | }); 73 | 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /test/integration-orientdb/bugs/40-object_instead_id/40-object_instead_id.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | _ = require('lodash'), 3 | utils = require('../../../../lib/utils'); 4 | 5 | var self = this; 6 | 7 | describe('Bug #40: Returning object instead of id', function() { 8 | before(function (done) { 9 | var fixtures = { 10 | Profile40Fixture: require('./40.profile.fixture'), 11 | SubprofileFixture: require('./40.subprofile.fixture'), 12 | ProfileconnectionFixture: require('./40.profileconnection.fixture') 13 | }; 14 | CREATE_TEST_WATERLINE(self, 'test_bug_40', fixtures, done); 15 | }); 16 | after(function (done) { 17 | DELETE_TEST_WATERLINE('test_bug_40', done); 18 | }); 19 | 20 | describe('find a profile', function() { 21 | ///////////////////////////////////////////////////// 22 | // TEST SETUP 23 | //////////////////////////////////////////////////// 24 | 25 | var profileRecord, subprofileRecord; 26 | 27 | before(function(done) { 28 | self.collections.Profile40.create({ alias: 'juanito', birthday: "1-01-1980" }, function(err, profile) { 29 | if(err) { return done(err); } 30 | profileRecord = profile; 31 | self.collections.Subprofile.create({ name: 'juan' }, function(err, subprofile) { 32 | if(err) { return done(err); } 33 | subprofileRecord = subprofile; 34 | 35 | profileRecord.profiles.add(subprofileRecord.id); 36 | profileRecord.save(function(err){ 37 | if(err) { return done(err); } 38 | done(); 39 | }); 40 | }); 41 | }); 42 | }); 43 | 44 | 45 | ///////////////////////////////////////////////////// 46 | // TEST METHODS 47 | //////////////////////////////////////////////////// 48 | 49 | it('should return a profile with a subprofile', function(done) { 50 | self.collections.Profile40.find() 51 | .populate('profiles') 52 | .then(function(profiles){ 53 | assert.equal(profiles.length, 1); 54 | assert.equal(profiles[0].id, profileRecord.id); 55 | done(); 56 | }) 57 | .catch(done); 58 | }); 59 | 60 | 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /test/integration-orientdb/tests/associations/manyThrough.update.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | _ = require('lodash'), 3 | utils = require('../../../../lib/utils'); 4 | 5 | describe('Association Interface', function() { 6 | 7 | describe('n:m through association :: .update', function() { 8 | 9 | ///////////////////////////////////////////////////// 10 | // TEST SETUP 11 | //////////////////////////////////////////////////// 12 | 13 | var stadiumRecord, teamRecord; 14 | 15 | before(function(done) { 16 | Associations.Stadium.create({ name: 'update stadium' }, function(err, stadium) { 17 | if(err) return done(err); 18 | stadiumRecord = stadium; 19 | Associations.Team.create({ name: 'populate team', mascot: 'elephant' }, function(err, team) { 20 | if(err) return done(err); 21 | teamRecord = team; 22 | stadiumRecord.teams.add(teamRecord.id); 23 | stadiumRecord.save(function(err){ 24 | assert(!err, err); 25 | done(); 26 | }); 27 | }); 28 | }); 29 | }); 30 | 31 | ///////////////////////////////////////////////////// 32 | // TEST METHODS 33 | //////////////////////////////////////////////////// 34 | describe('update operations should not impact associations', function() { 35 | 36 | it('update record without populate with association using collection.update()', function(done) { 37 | Associations.Stadium.findOne(stadiumRecord.id) 38 | .then(function(stadium){ 39 | assert.equal(stadium.teams.length, 0); 40 | assert.equal(stadium.name, 'update stadium'); 41 | 42 | //var updatedStadium = _.merge(stadium, { name: 'update stadium updated' }); 43 | return Associations.Stadium.update(stadiumRecord.id, { name: 'update stadium updated' }); 44 | }) 45 | .then(function(){ 46 | return Associations.Stadium.findOne(stadiumRecord.id) 47 | .populate('teams'); 48 | }) 49 | .then(function(updatedStadium){ 50 | assert.equal(updatedStadium.name, 'update stadium updated'); 51 | assert.equal(updatedStadium.teams.length, 1); 52 | done(); 53 | }) 54 | .catch(done); 55 | }); 56 | 57 | }); 58 | }); 59 | }); 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /test/integration-orientdb/tests/adapterCustomMethods/getDB_Server.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | _ = require('lodash'); 3 | 4 | describe('Adapter Custom Methods', function() { 5 | 6 | describe('getDB', function() { 7 | 8 | ///////////////////////////////////////////////////// 9 | // TEST SETUP 10 | //////////////////////////////////////////////////// 11 | 12 | var user; 13 | 14 | before(function(done) { 15 | Associations.Friend.create({ name: 'friend getDB' }, function(err, createdUser) { 16 | if(err) return done(err); 17 | 18 | user = createdUser; 19 | done(); 20 | }); 21 | }); 22 | 23 | after(function(done) { 24 | Associations.Friend.destroy(user.id, done); 25 | }); 26 | 27 | 28 | describe('query user', function() { 29 | 30 | ///////////////////////////////////////////////////// 31 | // TEST METHODS 32 | //////////////////////////////////////////////////// 33 | 34 | it('should return user', function(done) { 35 | Associations.Friend.getDB() 36 | .select().from('friendTable').where({name: 'friend getDB'}).one() 37 | .then(function(retrievedUser){ 38 | assert.equal(retrievedUser['@rid'].toString(), user.id); 39 | done(); 40 | }) 41 | .catch(done); 42 | }); 43 | 44 | }); 45 | 46 | }); 47 | 48 | 49 | describe('getServer', function() { 50 | describe('list databases', function() { 51 | 52 | ///////////////////////////////////////////////////// 53 | // TEST METHODS 54 | //////////////////////////////////////////////////// 55 | 56 | it('should return more than 0 dbs', function(done) { 57 | Associations.Friend.getServer() 58 | .list() 59 | .then(function (dbs) { 60 | assert(dbs.length >= 1); 61 | done(); 62 | }) 63 | .catch(done); 64 | }); 65 | }); 66 | }); 67 | 68 | 69 | describe('native', function() { 70 | describe('get native oriento collection', function() { 71 | 72 | ///////////////////////////////////////////////////// 73 | // TEST METHODS 74 | //////////////////////////////////////////////////// 75 | 76 | it('should return the collection\'s class name', function(done) { 77 | var collection = Associations.Friend.native(); 78 | assert(collection.name, 'friendTable'); 79 | done(); 80 | }); 81 | }); 82 | }); 83 | }); -------------------------------------------------------------------------------- /lib/collection/vertex.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var utils = require('../utils'), 4 | _ = require('lodash'), 5 | Document = require('./document'), 6 | log = require('debug-logger')('sails-orientdb:vertex'); 7 | 8 | /** 9 | * Manage A Vertex 10 | * 11 | * @param {Object} definition 12 | * @param {Connection} connection 13 | * @api public 14 | */ 15 | var Vertex = module.exports = utils.extend(Document, function() { 16 | Document.apply(this, arguments); 17 | 18 | // Set the orientdb super class ('document' / V / E) for this document 19 | this.superClass = 'V'; 20 | 21 | // Set a class command that will be passed to Oriento ( 'undefined' / VERTEX / EDGE) 22 | this.classCommand = 'VERTEX'; 23 | }); 24 | 25 | 26 | 27 | ///////////////////////////////////////////////////////////////////////////////// 28 | // PUBLIC METHODS 29 | ///////////////////////////////////////////////////////////////////////////////// 30 | 31 | 32 | /** 33 | * Destroy Documents 34 | * 35 | * @param {Object} criteria 36 | * @param {Function} callback 37 | * @api public 38 | */ 39 | Vertex.prototype.destroy = function destroy(criteria, cb) { 40 | var self = this; 41 | cb = cb || _.noop; 42 | 43 | // TODO: should be in a transaction 44 | self.find(criteria, function(err, results){ 45 | if(err){ return cb(err); } 46 | 47 | if(results.length === 0){ return cb(null, results); } 48 | 49 | var rids = _.map(results, 'id'); 50 | log.debug('Destroy rids: ' + rids); 51 | 52 | self.connection.db.delete(self.classCommand) 53 | .where('@rid in [' + rids.join(',') + ']') 54 | .one() 55 | .then(function (count) { 56 | if(parseInt(count) !== rids.length){ 57 | return cb(new Error('Deleted count [' + count + '] does not match rids length [' + rids.length + 58 | '], some vertices may not have been deleted')); 59 | } 60 | cb(null, results); 61 | }) 62 | .error(cb); 63 | }); 64 | }; 65 | 66 | 67 | 68 | //Deletes a collection from database 69 | Vertex.prototype.drop = function (relations, cb) { 70 | var self = this; 71 | 72 | // If class doesn't exist don't delete records 73 | if(!self.databaseClass){ 74 | return self.$super.prototype.drop.call(self, relations, cb); 75 | } 76 | 77 | self.connection.db.delete(self.classCommand, self.tableName).one() 78 | .then(function (count) { 79 | log.debug('Drop [' + self.tableName + '], deleted records: ' + count); 80 | self.$super.prototype.drop.call(self, relations, cb); 81 | }) 82 | .error(cb); 83 | }; 84 | 85 | -------------------------------------------------------------------------------- /test/integration-orientdb/tests/define/indexes.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | _ = require('lodash'), 3 | Oriento = require('orientjs'); 4 | 5 | describe('Define related Operations', function() { 6 | 7 | describe('Indexes', function() { 8 | 9 | ///////////////////////////////////////////////////// 10 | // TEST SETUP 11 | //////////////////////////////////////////////////// 12 | 13 | ///////////////////////////////////////////////////// 14 | // TEST METHODS 15 | //////////////////////////////////////////////////// 16 | 17 | function testIndex(attributeName, indexType, cb){ 18 | Associations.Indexes.getDB(function(db) { 19 | db.index.get('indexesTable.' + attributeName) 20 | .then(function(index) { 21 | assert.equal(index.name, 'indexesTable.' + attributeName); 22 | assert.equal(index.type, indexType); 23 | cb(); 24 | }) 25 | .error(cb); 26 | }); 27 | } 28 | 29 | it('should properly create unique index', function(done) { 30 | testIndex('indexUnique', 'UNIQUE', done); 31 | }); 32 | 33 | it('should properly create not unique index', function(done) { 34 | testIndex('indexDuplicates', 'NOTUNIQUE', done); 35 | }); 36 | 37 | it('should properly create fulltext index', function(done) { 38 | testIndex('indexFulltext', 'FULLTEXT', done); 39 | }); 40 | 41 | it('should properly create dictionary index', function(done) { 42 | testIndex('indexDictionary', 'DICTIONARY', done); 43 | }); 44 | 45 | it('should properly create unique hash index', function(done) { 46 | testIndex('indexUniqueHash', 'UNIQUE_HASH_INDEX', done); 47 | }); 48 | 49 | it('should properly create not unique hash index', function(done) { 50 | testIndex('indexNotUniqueHash', 'NOTUNIQUE_HASH_INDEX', done); 51 | }); 52 | 53 | it('should properly create fulltext hash index', function(done) { 54 | testIndex('indexFulltextHash', 'FULLTEXT_HASH_INDEX', done); 55 | }); 56 | 57 | it('should properly create dictionary hash index', function(done) { 58 | testIndex('indexDictionaryHash', 'DICTIONARY_HASH_INDEX', done); 59 | }); 60 | 61 | it('should properly create Link property in one-to-many association', function(done) { 62 | Associations.Indexes.native(function(klass) { 63 | klass.property.get('props') 64 | .then(function(property) { 65 | assert.equal(Oriento.types[property.type], 'Link'); 66 | assert.equal(property.linkedClass, 'propertiesTable'); 67 | done(); 68 | }) 69 | .error(done); 70 | }); 71 | }); 72 | 73 | 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /test/integration-orientdb/tests/adapterCustomMethods/increment.test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | _ = require('lodash'), 3 | async = require('async'); 4 | 5 | describe('Adapter Custom Methods', function () { 6 | 7 | describe('increment', function () { 8 | 9 | ///////////////////////////////////////////////////// 10 | // TEST SETUP 11 | //////////////////////////////////////////////////// 12 | 13 | before(function (done) { 14 | Associations.Counter.create({ name: 'counter1' }, function (err, counter) { 15 | if (err) return done(err); 16 | 17 | assert(counter); 18 | assert.strictEqual(counter.value, 0); 19 | done(); 20 | }); 21 | }); 22 | 23 | 24 | describe('counter', function () { 25 | 26 | ///////////////////////////////////////////////////// 27 | // TEST METHODS 28 | //////////////////////////////////////////////////// 29 | 30 | var lastCount = 0; 31 | 32 | it('should increment a field using the default value', function (done) { 33 | Associations.Counter.increment({ name: 'counter1' }, 'value', function (err, counter) { 34 | assert(!err); 35 | lastCount++; 36 | assert.strictEqual(counter.value, lastCount); 37 | done(); 38 | }); 39 | }); 40 | 41 | it('should increment a field using the specified positive value', function (done) { 42 | Associations.Counter.increment({ name: 'counter1' }, 'value', 2) 43 | .then(function (counter) { 44 | lastCount = lastCount + 2; 45 | assert.strictEqual(counter.value, lastCount); 46 | done(); 47 | }).catch(done); 48 | }); 49 | 50 | it('should increment a field using the specified negative value', function (done) { 51 | Associations.Counter.increment({ name: 'counter1' }, 'value', -1) 52 | .then(function (counter) { 53 | lastCount--; 54 | assert.strictEqual(counter.value, lastCount); 55 | done(); 56 | }).catch(done); 57 | }); 58 | 59 | it('should increment multiple times without returning duplicates', function (done) { 60 | var opsNumber = 50; 61 | async.map(Array.apply(0, Array(opsNumber)), function (val, next){ 62 | Associations.Counter.increment({ name: 'counter1' }, 'value') 63 | .then(function (counter) { 64 | next(null, counter.value); 65 | }).catch(next); 66 | }, 67 | function complete(err, results){ 68 | assert.equal(err, null); 69 | 70 | var uniqueCounts = _.uniq(results); 71 | assert.strictEqual(uniqueCounts.length, opsNumber); 72 | done(); 73 | }); 74 | 75 | 76 | }); 77 | 78 | }); 79 | }); 80 | }); -------------------------------------------------------------------------------- /test/integration-orientdb/tests/adapterCustomMethods/createEdge.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | _ = require('lodash'); 3 | 4 | describe('Adapter Custom Methods', function() { 5 | 6 | describe('createEdge', function() { 7 | 8 | ///////////////////////////////////////////////////// 9 | // TEST SETUP 10 | //////////////////////////////////////////////////// 11 | 12 | var user, user2, profile, profile2; 13 | 14 | before(function(done) { 15 | Associations.User_resource.create([{ name: 'user_create_1' }, { name: 'user_create_2' }], function(err, createdUsers) { 16 | if(err) return done(err); 17 | 18 | user = createdUsers[0]; 19 | user2 = createdUsers[1]; 20 | 21 | Associations.Profile.create([{ name: 'profile_create_1' }, { name: 'profile_create_2' }], function(err, createdProfiles) { 22 | if(err) return done(err); 23 | 24 | profile = createdProfiles[0]; 25 | profile2 = createdProfiles[1]; 26 | 27 | done(); 28 | }); 29 | }); 30 | }); 31 | 32 | 33 | describe('between two vertices', function() { 34 | 35 | ///////////////////////////////////////////////////// 36 | // TEST METHODS 37 | //////////////////////////////////////////////////// 38 | 39 | 40 | it('should link a user to a profile through an edge', function(done) { 41 | Associations.User_resource.createEdge(user.id, profile.id, null, function(err, edge){ 42 | 43 | assert(!!edge); 44 | assert.equal(edge.out, user.id); 45 | assert.equal(edge.in, profile.id); 46 | 47 | Associations.User_resource.getDB(function(db){ 48 | db 49 | .query('select expand(out()) from ' + user.id) 50 | .then(function(resultSet){ 51 | assert(_.isArray(resultSet), 'is array'); 52 | assert.equal(resultSet[0]['@rid'].toString(), profile.id); 53 | done(); 54 | }) 55 | .catch(done); 56 | }); 57 | }); 58 | }); 59 | 60 | it('should link a user to a profile through an edge using promise', function(done) { 61 | Associations.User_resource.createEdge(user2.id, profile2.id, null) 62 | .then(function(edge){ 63 | assert(!!edge); 64 | assert.equal(edge.out, user2.id); 65 | assert.equal(edge.in, profile2.id); 66 | 67 | Associations.User_resource.getDB(function(db){ 68 | db 69 | .query('select expand(out()) from ' + user2.id) 70 | .then(function(resultSet){ 71 | assert(_.isArray(resultSet), 'is array'); 72 | assert.equal(resultSet[0]['@rid'].toString(), profile2.id); 73 | done(); 74 | }) 75 | .catch(done); 76 | }); 77 | }) 78 | .catch(done); 79 | }); 80 | 81 | }); 82 | 83 | }); 84 | }); -------------------------------------------------------------------------------- /test/integration-orientdb/tests/adapterCustomMethods/query.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | _ = require('lodash'); 3 | 4 | describe('Adapter Custom Methods', function() { 5 | 6 | describe('query', function() { 7 | 8 | ///////////////////////////////////////////////////// 9 | // TEST SETUP 10 | //////////////////////////////////////////////////// 11 | 12 | var user; 13 | 14 | before(function(done) { 15 | Associations.Friend.create({ name: 'friend query' }, function(err, createdUser) { 16 | if(err) return done(err); 17 | 18 | user = createdUser; 19 | done(); 20 | }); 21 | }); 22 | 23 | after(function(done) { 24 | Associations.Friend.destroy(user.id, done); 25 | }); 26 | 27 | 28 | describe('query user', function() { 29 | 30 | ///////////////////////////////////////////////////// 31 | // TEST METHODS 32 | //////////////////////////////////////////////////// 33 | 34 | it('should return user', function(done) { 35 | Associations.Friend.query("SELECT FROM friendTable WHERE name='friend query'", function(err, retrievedUsers){ 36 | if (err) { done(err); } 37 | assert.equal(retrievedUsers.length, 1); 38 | assert.equal(retrievedUsers[0].id, user.id); 39 | assert(!retrievedUsers[0]['@rid']); 40 | done(); 41 | }); 42 | }); 43 | 44 | it('should return user using parameterized query', function(done) { 45 | Associations.Friend.query("SELECT FROM friendTable WHERE name=:name", { 46 | params: { 47 | name: 'friend query' 48 | } 49 | }, function(err, retrievedUsers){ 50 | if (err) { done(err); } 51 | assert.equal(retrievedUsers.length, 1); 52 | assert.equal(retrievedUsers[0].id, user.id); 53 | assert(!retrievedUsers[0]['@rid']); 54 | done(); 55 | }); 56 | }); 57 | 58 | it('should return user in a promise', function(done) { 59 | Associations.Friend.query("SELECT FROM friendTable WHERE name='friend query'") 60 | .then(function(retrievedUsers){ 61 | assert.equal(retrievedUsers.length, 1); 62 | assert.equal(retrievedUsers[0].id, user.id); 63 | assert(!retrievedUsers[0]['@rid']); 64 | done(); 65 | }) 66 | .catch(done); 67 | }); 68 | 69 | it('should return user in a promise using parameterized query', function(done) { 70 | Associations.Friend.query("SELECT FROM friendTable WHERE name=:name", { 71 | params: { 72 | name: 'friend query' 73 | } 74 | }) 75 | .then(function(retrievedUsers){ 76 | assert.equal(retrievedUsers.length, 1); 77 | assert.equal(retrievedUsers[0].id, user.id); 78 | assert(!retrievedUsers[0]['@rid']); 79 | done(); 80 | }) 81 | .catch(done); 82 | }); 83 | 84 | }); 85 | 86 | }); 87 | 88 | 89 | }); -------------------------------------------------------------------------------- /test/integration-orientdb/tests/adapterCustomMethods/deleteEdges.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | _ = require('lodash'); 3 | 4 | describe('Adapter Custom Methods', function() { 5 | 6 | describe('deleteEdges', function() { 7 | 8 | ///////////////////////////////////////////////////// 9 | // TEST SETUP 10 | //////////////////////////////////////////////////// 11 | 12 | var user, profile; 13 | 14 | before(function(done) { 15 | Associations.User_resource.create({ name: 'the edge delete' }, function(err, createdUser) { 16 | if(err) return done(err); 17 | 18 | user = createdUser; 19 | 20 | Associations.Profile.create({ name: 'edge profile delete' }, function(err, createdProfile) { 21 | if(err) return done(err); 22 | 23 | profile = createdProfile; 24 | 25 | Associations.User_resource.createEdge(user.id, profile.id, null, function(err, edge){ 26 | if(err) return done(err); 27 | 28 | assert(edge); 29 | 30 | Associations.User_resource.createEdge(user.id, profile.id, { '@class': 'followsTable' }, function(err, edge){ 31 | if(err) return done(err); 32 | 33 | assert(edge); 34 | 35 | done(); 36 | }); 37 | }); 38 | 39 | }); 40 | }); 41 | }); 42 | 43 | 44 | describe('between two vertices', function() { 45 | 46 | ///////////////////////////////////////////////////// 47 | // TEST METHODS 48 | //////////////////////////////////////////////////// 49 | 50 | it('should delete an edge of class followsTable', function(done) { 51 | Associations.User_resource.deleteEdges(user.id, profile.id, { '@class': 'followsTable' }, function(err, count){ 52 | if(err) return done(err); 53 | 54 | assert.equal(count, 1); 55 | 56 | Associations.User_resource.getDB(function(db){ 57 | db 58 | .query('select expand(outE()) from ' + user.id) 59 | .then(function(resultSet){ 60 | assert(_.isArray(resultSet), 'is array'); 61 | assert.equal(resultSet.length, 1); 62 | assert.notEqual(resultSet[0]['@class'], 'followsTable'); 63 | done(); 64 | }) 65 | .catch(done); 66 | }); 67 | }); 68 | }); 69 | 70 | 71 | it('should delete an edge between a user and profile using a promise', function(done) { 72 | Associations.User_resource.deleteEdges(user.id, profile.id, null) 73 | .then(function(count){ 74 | 75 | assert.equal(count, 1); 76 | 77 | Associations.User_resource.getDB(function(db){ 78 | db 79 | .query('select expand(out()) from ' + user.id) 80 | .then(function(resultSet){ 81 | assert(_.isArray(resultSet), 'is array'); 82 | assert.equal(resultSet.length, 0); 83 | done(); 84 | }) 85 | .catch(done); 86 | }); 87 | }) 88 | .catch(done); 89 | }); 90 | 91 | }); 92 | 93 | }); 94 | }); -------------------------------------------------------------------------------- /test/integration-orientdb/tests/associations/manyToMany.joinTableName.find.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | _ = require('lodash'); 3 | 4 | describe('Association Interface', function() { 5 | 6 | describe('n:m association :: tableName attribute', function() { 7 | 8 | ///////////////////////////////////////////////////// 9 | // TEST SETUP 10 | //////////////////////////////////////////////////// 11 | 12 | var driverRecord; 13 | 14 | before(function(done) { 15 | Associations.Driver.create({ name: 'manymany find'}, function(err, driver) { 16 | if(err) return done(err); 17 | 18 | driverRecord = driver; 19 | 20 | var taxis = []; 21 | for(var i=0; i<2; i++) { 22 | driverRecord.taxis.add({ medallion: i }); 23 | } 24 | 25 | driverRecord.save(function(err) { 26 | if(err) return done(err); 27 | done(); 28 | }); 29 | }); 30 | }); 31 | 32 | ///////////////////////////////////////////////////// 33 | // TEST METHODS 34 | //////////////////////////////////////////////////// 35 | 36 | it('should return "drives" as join table name', function(done) { 37 | Associations.Driver_taxis__taxi_drivers.native(function(collection){ 38 | assert.equal(collection.name, 'drives'); 39 | done(); 40 | }); 41 | }); 42 | 43 | it('should return "E" (edge) as join table\'s super class', function(done) { 44 | Associations.Driver_taxis__taxi_drivers.native(function(collection){ 45 | assert.equal(collection.superClass, 'E'); 46 | done(); 47 | }); 48 | }); 49 | 50 | it('should return taxis when the populate criteria is added', function(done) { 51 | Associations.Driver.find({ name: 'manymany find' }) 52 | .populate('taxis') 53 | .exec(function(err, drivers) { 54 | assert(!err); 55 | 56 | assert(Array.isArray(drivers)); 57 | assert(drivers.length === 1); 58 | assert(Array.isArray(drivers[0].taxis)); 59 | assert(drivers[0].taxis.length === 2); 60 | 61 | done(); 62 | }); 63 | }); 64 | 65 | it('should not return a taxis object when the populate is not added', function(done) { 66 | Associations.Driver.find() 67 | .exec(function(err, drivers) { 68 | assert(!err); 69 | 70 | var obj = drivers[0].toJSON(); 71 | assert(!obj.taxis); 72 | 73 | done(); 74 | }); 75 | }); 76 | 77 | it('should call toJSON on all associated records if available', function(done) { 78 | Associations.Driver.find({ name: 'manymany find' }) 79 | .populate('taxis') 80 | .exec(function(err, drivers) { 81 | assert(!err); 82 | 83 | var obj = drivers[0].toJSON(); 84 | assert(!obj.name); 85 | 86 | assert(Array.isArray(obj.taxis)); 87 | assert(obj.taxis.length === 2); 88 | 89 | assert(obj.taxis[0].hasOwnProperty('createdAt')); 90 | assert(!obj.taxis[0].hasOwnProperty('medallion')); 91 | assert(obj.taxis[1].hasOwnProperty('createdAt')); 92 | assert(!obj.taxis[1].hasOwnProperty('medallion')); 93 | 94 | done(); 95 | }); 96 | }); 97 | 98 | }); 99 | }); 100 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | MOCHA_OPTS= --check-leaks --timeout 6000 3 | REPORTER = spec 4 | DB?=waterline-test-integration 5 | 6 | test: clean-all test-unit test-integration-all 7 | test-all: test test-integration-schemaless clean test-integration-documentdb 8 | 9 | 10 | test-integration-all: test-integration-orientdb test-integration 11 | 12 | 13 | test-integration-generic: 14 | @echo "\n\nNOTICE: If tests fail, please ensure you've set the correct credentials in test/test-connection.json\n" 15 | @echo "Running 'waterline-adapter-tests' integration tests..." 16 | 17 | test-integration: test-integration-generic 18 | @NODE_ENV=test node test/integration/runner.js 19 | 20 | test-integration-documentdb: test-integration-generic 21 | @echo DATABASE_TYPE=document 22 | @NODE_ENV=test DATABASE_TYPE=document node test/integration/runner.js 23 | 24 | test-integration-schemaless: test-integration-generic 25 | @echo SCHEMA=0 26 | @NODE_ENV=test SCHEMA=0 node test/integration/runner.js 27 | 28 | test-integration-orientdb: 29 | @echo "\n\nNOTICE: If tests fail, please ensure you've set the correct credentials in test/test-connection.json\n" 30 | @echo "Running waterline-orientdb integration tests..." 31 | @NODE_ENV=test ./node_modules/.bin/mocha \ 32 | --reporter $(REPORTER) \ 33 | --timeout 6000 --globals Associations,CREATE_TEST_WATERLINE,DELETE_TEST_WATERLINE \ 34 | test/integration-orientdb/*.js test/integration-orientdb/tests/**/*.js \ 35 | test/integration-orientdb/bugs/*.js test/integration-orientdb/bugs/**/*.js 36 | 37 | test-unit: 38 | @echo "\n\nRunning waterline-orientdb unit tests..." 39 | @NODE_ENV=test ./node_modules/.bin/mocha \ 40 | --reporter $(REPORTER) \ 41 | $(MOCHA_OPTS) \ 42 | test/unit/*.js test/unit/**/*.js 43 | 44 | coverage: clean-all 45 | @echo "\n\nRunning coverage report..." 46 | rm -rf coverage 47 | ./node_modules/istanbul/lib/cli.js cover --report none --dir coverage/unit \ 48 | ./node_modules/.bin/_mocha test/unit/*.js test/unit/**/*.js \ 49 | -- $(MOCHA_OPTS) 50 | ./node_modules/istanbul/lib/cli.js cover --report none --dir coverage/integration-orientdb \ 51 | ./node_modules/.bin/_mocha test/integration-orientdb/*.js test/integration-orientdb/tests/**/*.js \ 52 | -- --timeout 15000 --globals Associations 53 | ./node_modules/istanbul/lib/cli.js cover --report none --dir coverage/integration test/integration/runner.js 54 | ./node_modules/istanbul/lib/cli.js cover --report none --dir coverage/integration-document test/integration/runner.js document 55 | ./node_modules/istanbul/lib/cli.js report 56 | 57 | clean: 58 | @echo "\n\nDROPPING ALL COLLECTIONS from db: $(DB)" 59 | @echo "NOTICE: If operation fails, please ensure you've set the correct credentials in orientjs.opts file" 60 | @echo "Note: you can choose which db to drop by appending 'DB=', e.g. 'make clean DB=waterline-test-orientdb'\n" 61 | ./node_modules/.bin/orientjs db drop $(DB) || true 62 | 63 | clean-all: 64 | @echo "\n\nDROPPING DATABASES: waterline-test-integration, waterline-test-orientdb" 65 | @echo "NOTICE: If operation fails, please ensure you've set the correct credentials in orientjs.opts file\n" 66 | ./node_modules/.bin/orientjs db drop waterline-test-integration > /dev/null 2>&1 || true 67 | ./node_modules/.bin/orientjs db drop waterline-test-orientdb > /dev/null 2>&1 || true 68 | @echo "Done" 69 | 70 | .PHONY: coverage 71 | -------------------------------------------------------------------------------- /test/integration-orientdb/bugs/44-email_attribute.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | _ = require('lodash'), 3 | utils = require('../../../lib/utils'); 4 | 5 | var self = this; 6 | 7 | describe('Bug #44: Unrecognized Types', function() { 8 | before(function (done) { 9 | 10 | var fixtures = { 11 | UserFixture : { 12 | // Enforce model schema in the case of schemaless databases 13 | schema : true, 14 | identity : 'user', 15 | 16 | attributes : { 17 | // id : { 18 | // type : 'string', 19 | // primaryKey : true, 20 | // columnName : '@rid' 21 | // }, 22 | username : { 23 | type : 'string', 24 | unique : true 25 | }, 26 | email : { 27 | type : 'email', 28 | unique : true 29 | }, 30 | passports : { 31 | collection : 'Passport', 32 | via : 'user' 33 | } 34 | } 35 | }, 36 | PassportFixture : { 37 | identity : 'passport', 38 | 39 | attributes : { 40 | // id : { 41 | // type : 'string', 42 | // primaryKey : true, 43 | // columnName : '@rid' 44 | // }, 45 | protocol : { 46 | type : 'alphanumeric', 47 | required : true 48 | }, 49 | 50 | password : { 51 | type : 'string', 52 | minLength : 8 53 | }, 54 | provider : { 55 | type : 'alphanumericdashed' 56 | }, 57 | identifier : { 58 | type : 'string' 59 | }, 60 | tokens : { 61 | type : 'json' 62 | }, 63 | 64 | user : { 65 | model : 'User', 66 | required : true 67 | }, 68 | 69 | validatePassword : function(password, next) { 70 | next(null, password); 71 | } 72 | }, 73 | 74 | beforeCreate : function(passport, next) { 75 | next(null, passport); 76 | }, 77 | 78 | beforeUpdate : function(passport, next) { 79 | next(null, passport); 80 | } 81 | } 82 | }; 83 | 84 | 85 | CREATE_TEST_WATERLINE(self, 'test_bug_44', fixtures, done); 86 | }); 87 | after(function (done) { 88 | DELETE_TEST_WATERLINE('test_bug_44', done); 89 | }); 90 | 91 | describe('create user', function() { 92 | ///////////////////////////////////////////////////// 93 | // TEST SETUP 94 | //////////////////////////////////////////////////// 95 | 96 | ///////////////////////////////////////////////////// 97 | // TEST METHODS 98 | //////////////////////////////////////////////////// 99 | 100 | it('should create a user with e-mail', function(done) { 101 | self.collections.User.create({ email: 'email@example.com' }, function(err, user) { 102 | if(err) { return done(err); } 103 | assert.equal(user.email, 'email@example.com'); 104 | 105 | self.collections.Passport.create({ user: user.id, password: 'abcd5678', protocol: '80' }, function(err, passport) { 106 | if(err) { return done(err); } 107 | assert.equal(passport.password, 'abcd5678'); 108 | done(); 109 | 110 | }); 111 | }); 112 | }); 113 | 114 | 115 | }); 116 | }); 117 | -------------------------------------------------------------------------------- /example/associations/many-to-many.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Based on http://stackoverflow.com/questions/28985075/sails-orientdb-bi-directional-edge 3 | * Associations docs at https://github.com/balderdashy/waterline-docs/blob/master/associations.md 4 | * More info at https://github.com/balderdashy/waterline 5 | */ 6 | 7 | ////////////////////////////////////////////////////////////////////////////// 8 | // Example on how to run: 9 | // mkdir manyToMany 10 | // cd manyToMany 11 | // npm install waterline 12 | // npm install sails-orientdb 13 | // node node_modules/sails-orientdb/example/associations/many-to-many.js 14 | ////////////////////////////////////////////////////////////////////////////// 15 | 16 | var setupWaterline = require('../raw/bootstrap'); 17 | 18 | var connections = { 19 | associations : { 20 | adapter: 'sails-orientdb', 21 | host: 'localhost', 22 | port: 2424, 23 | user: 'root', 24 | password: 'root', 25 | database: 'example-waterline-manyToMany' 26 | } 27 | }; 28 | 29 | var collections = { 30 | questions: { 31 | tableName: 'questionsTable', 32 | identity: 'questions', 33 | connection: 'associations', 34 | 35 | attributes: { 36 | id: { type: 'string', primaryKey: true, columnName: '@rid'}, 37 | question : { type: 'string'}, 38 | // user: { model: "User", required: true }, 39 | answerOptions: {type: 'json'}, 40 | imagefile: {type:'string'}, 41 | answers: { 42 | collection: 'answer', 43 | via: 'questions', 44 | dominant:true 45 | } 46 | } 47 | }, 48 | 49 | answer: { 50 | tableName: 'answerTable', 51 | identity: 'answer', 52 | connection: 'associations', 53 | 54 | attributes: { 55 | id: { 56 | type: 'string', 57 | primaryKey: true, 58 | columnName: '@rid' 59 | }, 60 | answer: 'string', 61 | questions: { 62 | collection: 'questions', 63 | via: 'answers' 64 | } 65 | } 66 | } 67 | }; 68 | 69 | setupWaterline({ 70 | collections : collections, 71 | connections : connections 72 | }, function waterlineReady (err, ontology){ 73 | if (err) throw err; 74 | 75 | console.log('\nWaterline initialized\n'); 76 | 77 | var question1, answer1; 78 | 79 | ontology.collections.questions.create({ question: 'question1' }) 80 | .then(function(question){ 81 | question1 = question; 82 | 83 | return ontology.collections.answer.create([{ answer: 'answer1' }, { answer: 'answer2' }]); 84 | }) 85 | .then(function(answers){ 86 | answer1 = answers[0]; 87 | question1.answers.add(answers[0]); 88 | question1.answers.add(answers[1]); 89 | 90 | return question1.save(); 91 | }) 92 | .then(function(res){ 93 | return ontology.collections.questions.findOne(question1.id) 94 | .populate('answers'); 95 | }) 96 | .then(function(populatedQuestion){ 97 | console.log('Question', populatedQuestion.question, 'has the following answers:', populatedQuestion.answers, '\n'); 98 | 99 | return ontology.collections.answer.findOne(answer1.id) 100 | .populate('questions'); 101 | }) 102 | .then(function(populatedAnswer){ 103 | console.log('Answer', populatedAnswer.answer, 'has the following questions:', populatedAnswer.questions, '\n'); 104 | console.log('All done, have a nice day!\n'); 105 | process.exit(0); 106 | }); 107 | 108 | }); 109 | -------------------------------------------------------------------------------- /test/integration-orientdb/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module Dependencies 3 | */ 4 | var Waterline = require('waterline'); 5 | var _ = require('lodash'); 6 | var async = require('async'); 7 | var Adapter = require('../../'); 8 | 9 | var config = require('../test-connection.json'); 10 | config.database = 'waterline-test-orientdb'; 11 | config.options = config.options || {}; 12 | config.options.storage = "memory"; 13 | 14 | var instancesMap = {}; 15 | 16 | ///////////////////////////////////////////////////// 17 | // TEST SETUP 18 | //////////////////////////////////////////////////// 19 | 20 | global.CREATE_TEST_WATERLINE = function(context, testConfig, fixtures, cb){ 21 | cb = cb || _.noop; 22 | 23 | var waterline, ontology; 24 | 25 | var localConfig = _.cloneDeep(config); 26 | if(testConfig){ 27 | if(_.isString(testConfig)){ 28 | localConfig.database = testConfig; 29 | } else if(_.isObject(testConfig)){ 30 | _.merge(localConfig, testConfig); 31 | } 32 | } 33 | 34 | waterline = new Waterline(); 35 | 36 | // context variable 37 | context.collections = {}; 38 | context.waterline = waterline; 39 | 40 | Object.keys(fixtures).forEach(function(key) { 41 | fixtures[key].connection = localConfig.database; 42 | waterline.loadCollection(Waterline.Collection.extend(fixtures[key])); 43 | }); 44 | 45 | var Connections = { 46 | 'test': localConfig 47 | }; 48 | Connections.test.adapter = 'wl_tests'; 49 | 50 | var connections = {}; 51 | connections[localConfig.database] = _.clone(Connections.test); 52 | 53 | waterline.initialize({ adapters: { wl_tests: Adapter }, connections: connections }, function(err, _ontology) { 54 | if(err) return cb(err); 55 | 56 | ontology = _ontology; 57 | 58 | Object.keys(_ontology.collections).forEach(function(key) { 59 | var globalName = key.charAt(0).toUpperCase() + key.slice(1); 60 | context.collections[globalName] = _ontology.collections[key]; 61 | }); 62 | 63 | instancesMap[localConfig.database] = { 64 | waterline: waterline, 65 | ontology: ontology, 66 | config: localConfig 67 | }; 68 | 69 | cb(null, context.collections); 70 | }); 71 | }; 72 | 73 | 74 | global.DELETE_TEST_WATERLINE = function(testConfig, cb){ 75 | cb = cb || _.noop; 76 | 77 | var dbName = _.isString(testConfig) ? testConfig : testConfig.database; 78 | 79 | if(!instancesMap[dbName]) { return cb(new Error('Waterline instance not found for ' + dbName + '! Did you use the correct db name?')); }; 80 | 81 | var ontology = instancesMap[dbName].ontology; 82 | var waterline = instancesMap[dbName].waterline; 83 | var localConfig = instancesMap[dbName].config; 84 | 85 | function dropCollection(item, next) { 86 | if(!Adapter.hasOwnProperty('drop')) return next(); 87 | 88 | ontology.collections[item].drop(function(err) { 89 | if(err) return next(err); 90 | next(); 91 | }); 92 | } 93 | 94 | async.each(Object.keys(ontology.collections), dropCollection, function(err) { 95 | if(err) { 96 | console.log('ERROR dropping collections:', err); 97 | return cb(err); 98 | }; 99 | 100 | Adapter.getServer(localConfig.database, undefined, function(server){ 101 | server.drop({ 102 | name: localConfig.database, 103 | storage: localConfig.options.storage 104 | }) 105 | .then(function(){ 106 | waterline.teardown(cb); 107 | }) 108 | .catch(cb); 109 | }); 110 | }); 111 | 112 | }; 113 | -------------------------------------------------------------------------------- /test/integration-orientdb/bugs/116-test_edge_creation.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | 3 | var self = this; 4 | 5 | describe('Bug #116: Found class name null or empty', function () { 6 | before(function (done) { 7 | 8 | var fixtures = { 9 | AnswerFixture: { 10 | tableName: 'Answer', 11 | //identity: 'answer', 12 | 13 | attributes: { 14 | id: { 15 | type: 'string', 16 | primaryKey: true, 17 | columnName: '@rid' 18 | }, 19 | Answer: { 20 | type: 'JSON' 21 | }, 22 | question: { 23 | collection: 'Questions', 24 | via: 'answers', 25 | } 26 | } 27 | }, 28 | 29 | QuestionsFixture: { 30 | tableName: 'Questions', 31 | //identity:'questions', 32 | joinTableNames: { 33 | answers: 'answered' 34 | }, 35 | 36 | attributes: { 37 | id: { 38 | type: 'string', 39 | primaryKey: true, 40 | columnName: '@rid' 41 | }, 42 | question: { 43 | type: 'string' 44 | }, 45 | answers: { 46 | collection: 'Answer', 47 | via: 'question', 48 | dominant: true 49 | } 50 | } 51 | } 52 | 53 | }; 54 | 55 | CREATE_TEST_WATERLINE(self, 'test_bug_116', fixtures, done); 56 | }); 57 | after(function (done) { 58 | DELETE_TEST_WATERLINE('test_bug_116', done); 59 | }); 60 | 61 | describe('Create answer', function () { 62 | 63 | ///////////////////////////////////////////////////// 64 | // TEST SETUP 65 | //////////////////////////////////////////////////// 66 | 67 | var questionRecord; 68 | 69 | 70 | before(function (done) { 71 | self.collections.Questions.create({ 72 | question: 'This should work, shouldnt it?' 73 | }, function (err, question) { 74 | 75 | if (err) { 76 | return done(err); 77 | } 78 | 79 | questionRecord = question; 80 | done(); 81 | 82 | }); 83 | }); 84 | 85 | 86 | ///////////////////////////////////////////////////// 87 | // TEST METHODS 88 | //////////////////////////////////////////////////// 89 | 90 | it('should add answer and create edge answered from answer to questions', function (done) { 91 | 92 | 93 | self.collections.Answer.create({ 94 | question:questionRecord.id, 95 | Answer: {binary:'Yes'} 96 | }, function (err, question) { 97 | 98 | if (err) { 99 | return done(err); 100 | } 101 | 102 | self.collections.Answer.query('select * from answered', function (err, answered) { 103 | 104 | if (err) { 105 | return done(err); 106 | } 107 | 108 | assert(answered); 109 | done(); 110 | 111 | }); 112 | 113 | }); 114 | 115 | 116 | }); 117 | 118 | 119 | }); 120 | }); 121 | -------------------------------------------------------------------------------- /test/integration-orientdb/tests/adapterCustomMethods/decodeURIComponent.test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | _ = require('lodash'); 3 | 4 | var self = this; 5 | 6 | describe('decodeURIComponent: decode the id', function() { 7 | 8 | var fixtures = { 9 | UserFixture : { 10 | identity : 'user', 11 | 12 | attributes : { 13 | username : 'string', 14 | email : 'email', 15 | } 16 | }, 17 | BlueprintsUserFixture : { 18 | identity : 'blue_user', 19 | 20 | attributes : { 21 | id : { 22 | type : 'string', 23 | primaryKey : true, 24 | columnName : '@rid' 25 | }, 26 | username : 'string', 27 | email : 'email', 28 | } 29 | } 30 | }; 31 | 32 | var config = { 33 | database: 'test_decodeURIComponent', 34 | options: { 35 | decodeURIComponent: true 36 | } 37 | } 38 | 39 | before(function (done) { 40 | CREATE_TEST_WATERLINE(self, config, fixtures, done); 41 | }); 42 | after(function (done) { 43 | DELETE_TEST_WATERLINE(config, done); 44 | }); 45 | 46 | describe('find user', function() { 47 | 48 | ///////////////////////////////////////////////////// 49 | // TEST METHODS 50 | //////////////////////////////////////////////////// 51 | var userRecord, encodedUserId, encodedUser2Id, blueUserRecord, encodedBlueUserId; 52 | 53 | before(function (done) { 54 | self.collections.User.create([{ email: 'user1@example.com' }, { email: 'user2@example.com' }], function(err, users) { 55 | if(err) { return done(err); } 56 | userRecord = users[0]; 57 | encodedUserId = encodeURIComponent(userRecord.id); 58 | encodedUser2Id = encodeURIComponent(users[1].id); 59 | self.collections.Blue_user.create({ email: 'blue@example.com' }, function(err, blueUser) { 60 | if(err) { return done(err); } 61 | blueUserRecord = blueUser; 62 | encodedBlueUserId = encodeURIComponent(blueUserRecord.id); 63 | done(); 64 | }); 65 | }); 66 | }); 67 | 68 | 69 | it('regression test: should retrieve user by id', function(done) { 70 | self.collections.User.findOne(userRecord.id, function(err, user) { 71 | if(err) { return done(err); } 72 | assert.equal(user.email, 'user1@example.com'); 73 | done(); 74 | }); 75 | }); 76 | 77 | it('should retrieve user with encoded id', function(done) { 78 | self.collections.User.findOne(encodedUserId, function(err, user) { 79 | if(err) { return done(err); } 80 | assert.equal(user.email, 'user1@example.com'); 81 | done(); 82 | }); 83 | }); 84 | 85 | it('should retrieve 2 users with encoded id', function(done) { 86 | self.collections.User.find([encodedUserId, encodedUser2Id], function(err, users) { 87 | if(err) { return done(err); } 88 | assert.equal(users[0].email, 'user1@example.com'); 89 | assert.equal(users[1].email, 'user2@example.com'); 90 | done(); 91 | }); 92 | }); 93 | 94 | it('regression test: should retrieve blueprints user by id', function(done) { 95 | self.collections.Blue_user.findOne(blueUserRecord.id, function(err, user) { 96 | if(err) { return done(err); } 97 | assert.equal(user.email, 'blue@example.com'); 98 | done(); 99 | }); 100 | }); 101 | 102 | it('should retrieve blueprints user with encoded id', function(done) { 103 | self.collections.Blue_user.findOne(encodedBlueUserId, function(err, user) { 104 | if(err) { return done(err); } 105 | assert.equal(user.email, 'blue@example.com'); 106 | done(); 107 | }); 108 | }); 109 | 110 | 111 | }); 112 | }); 113 | -------------------------------------------------------------------------------- /test/integration-orientdb/tests/config/database.test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | _ = require('lodash'); 3 | 4 | var self = this, 5 | fixtures, 6 | config, 7 | configConn = require('../../../test-connection.json'); 8 | 9 | describe('Config tests', function() { 10 | before(function (done) { 11 | 12 | fixtures = { 13 | UserFixture : { 14 | identity : 'user', 15 | attributes : { 16 | name : 'string' 17 | } 18 | }, 19 | ThingFixture : { 20 | identity : 'thing', 21 | 22 | attributes : { 23 | name : 'string' 24 | } 25 | } 26 | }; 27 | 28 | config = { 29 | user : configConn.user, 30 | password : configConn.password, 31 | database : 'test_config_db' 32 | }; 33 | 34 | CREATE_TEST_WATERLINE(self, config, fixtures, done); 35 | }); 36 | after(function (done) { 37 | DELETE_TEST_WATERLINE(config, done); 38 | }); 39 | 40 | describe('database', function() { 41 | 42 | describe('username', function() { 43 | 44 | ///////////////////////////////////////////////////// 45 | // TEST SETUP 46 | //////////////////////////////////////////////////// 47 | 48 | before(function (done) { 49 | // db created, let's close the connection so we can test logins 50 | self.waterline.teardown(done); 51 | }); 52 | 53 | after(function (done) { 54 | // let's log off last user because it may not have privileges to drop the db later on 55 | self.waterline.teardown(function(err){ 56 | if(err) { return done(err); } 57 | // and now we logon with original config 58 | CREATE_TEST_WATERLINE(self, config, fixtures, done); 59 | }); 60 | }); 61 | 62 | ///////////////////////////////////////////////////// 63 | // TEST METHODS 64 | //////////////////////////////////////////////////// 65 | it('should be the same as connection username', function(done) { 66 | CREATE_TEST_WATERLINE(self, config, fixtures, function(err){ 67 | if(err) { return done(err); } 68 | self.collections.User.getDB(function(db){ 69 | assert.equal(db.username, config.user); 70 | done(); 71 | }); 72 | }); 73 | }); 74 | 75 | it('should be the same as databaseUser', function(done) { 76 | self.waterline.teardown(function(err){ 77 | if(err) { return done(err); } 78 | 79 | var newConfig = _.cloneDeep(config); 80 | 81 | newConfig.options = { 82 | databaseUser : 'admin', 83 | databasePassword : 'admin', 84 | }; 85 | 86 | CREATE_TEST_WATERLINE(self, newConfig, fixtures, function(err){ 87 | if(err) { return done(err); } 88 | self.collections.User.getDB(function(db){ 89 | assert.equal(db.username, 'admin'); 90 | done(); 91 | }); 92 | }); 93 | }); 94 | }); 95 | 96 | it('should be able to connect with database credentials only', function(done) { 97 | self.waterline.teardown(function (err) { 98 | if (err) { return done(err); } 99 | 100 | var newConfig = _.cloneDeep(configConn); 101 | newConfig.user = undefined; 102 | newConfig.password = undefined; 103 | newConfig.options.databaseUser = newConfig.databaseTestUser || 'admin'; 104 | newConfig.options.databasePassword = newConfig.databaseTestPassword || 'admin'; 105 | 106 | CREATE_TEST_WATERLINE(self, newConfig, fixtures, function(err){ 107 | if (err) { return done(err); } 108 | 109 | done(); 110 | }); 111 | }); 112 | }) 113 | }); 114 | }); 115 | }); 116 | -------------------------------------------------------------------------------- /test/integration-orientdb/tests/define/properties.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | _ = require('lodash'), 3 | Oriento = require('orientjs'); 4 | 5 | describe('Define related Operations', function() { 6 | 7 | describe('Property creation', function() { 8 | 9 | ///////////////////////////////////////////////////// 10 | // TEST SETUP 11 | //////////////////////////////////////////////////// 12 | 13 | var klass; 14 | 15 | before(function(done) { 16 | Associations.Properties.native(function(nativeClass) { 17 | klass = nativeClass; 18 | done(); 19 | }); 20 | }); 21 | 22 | 23 | ///////////////////////////////////////////////////// 24 | // TEST METHODS 25 | //////////////////////////////////////////////////// 26 | 27 | it('should properly create mandatory property', function(done) { 28 | klass.property.get('propRequired') 29 | .then(function(property) { 30 | assert.equal(property.name, 'propRequired'); 31 | assert.equal(property.mandatory, true); 32 | done(); 33 | }) 34 | .error(done); 35 | }); 36 | 37 | it('should properly create string property from string', function(done) { 38 | klass.property.get('stringProp') 39 | .then(function(property) { 40 | assert.equal(property.name, 'stringProp'); 41 | assert.equal(Oriento.types[property.type], 'String'); 42 | done(); 43 | }) 44 | .error(done); 45 | }); 46 | 47 | it('should properly create string property from text', function(done) { 48 | klass.property.get('textProp') 49 | .then(function(property) { 50 | assert.equal(Oriento.types[property.type], 'String'); 51 | done(); 52 | }) 53 | .error(done); 54 | }); 55 | 56 | it('should properly create float property from float', function(done) { 57 | klass.property.get('floatProp') 58 | .then(function(property) { 59 | assert.equal(Oriento.types[property.type], 'Float'); 60 | done(); 61 | }) 62 | .error(done); 63 | }); 64 | 65 | it('should properly create Embedded property from json', function(done) { 66 | klass.property.get('jsonProp') 67 | .then(function(property) { 68 | assert.equal(Oriento.types[property.type], 'Embedded'); 69 | done(); 70 | }) 71 | .error(done); 72 | }); 73 | 74 | it('should properly create EmbeddedList property from array', function(done) { 75 | klass.property.get('arrayProp') 76 | .then(function(property) { 77 | assert.equal(Oriento.types[property.type], 'EmbeddedList'); 78 | done(); 79 | }) 80 | .error(done); 81 | }); 82 | 83 | it('should properly create Link property from model', function(done) { 84 | klass.property.get('modelProp') 85 | .then(function(property) { 86 | assert.equal(Oriento.types[property.type], 'Link'); 87 | assert.equal(property.linkedClass, 'indexesTable'); 88 | done(); 89 | }) 90 | .error(done); 91 | }); 92 | 93 | it('should properly create String property from email', function(done) { 94 | klass.property.get('emailProp') 95 | .then(function(property) { 96 | assert.equal(Oriento.types[property.type], 'String'); 97 | done(); 98 | }) 99 | .error(done); 100 | }); 101 | 102 | 103 | 104 | // Not sure this can happen seen it's only required a Links exists on the associated table 105 | // it('should properly create LinkSet property from collection', function(done) { 106 | // klass.property.get('collectionProp') 107 | // .then(function(property) { 108 | // assert.equal(Oriento.types[property.type], 'LinkSet'); 109 | // assert.equal(property.linkedClass, 'indexesTable'); 110 | // done(); 111 | // }) 112 | // .error(done); 113 | // }); 114 | 115 | 116 | }); 117 | }); -------------------------------------------------------------------------------- /test/unit/utils.extend.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Test dependencies 3 | */ 4 | var assert = require('assert'), 5 | utils = require('../../lib/utils'); 6 | 7 | 8 | describe('utils helper class', function() { 9 | 10 | var Shape; 11 | 12 | before(function(done){ 13 | Shape = function(val1, val2) { 14 | this.x = val1; 15 | this.y = val2; 16 | }; 17 | Shape.shapeProperty = 'shape'; 18 | Shape.willBeOverriden = 'shape'; 19 | Shape.prototype.protoShape = 'shape'; 20 | Shape.prototype.protoOverride = 'shape'; 21 | Shape.prototype.overrideMethod = function(){ 22 | return 'shape'; 23 | }; 24 | 25 | done(); 26 | }); 27 | 28 | describe('extend:', function() { 29 | 30 | it('should extend classes without source', function(done) { 31 | var Extended = utils.extend(Shape); 32 | var circle = new Extended(1, 2); 33 | assert(circle instanceof Extended); 34 | assert(circle instanceof Shape); 35 | assert.equal(circle.y, 2); 36 | done(); 37 | }); 38 | 39 | it('should extend classes from object with constructor', function(done) { 40 | var Extended = utils.extend(Shape, { constructor: function(val){ this.z = val; } }); 41 | var circle = new Extended(1); 42 | assert(circle instanceof Extended); 43 | assert(circle instanceof Shape); 44 | assert.equal(circle.z, 1); 45 | done(); 46 | }); 47 | 48 | it('should extend classes from object', function(done) { 49 | var Circle = { 50 | willBeOverriden: 'circle', 51 | prototype: { 52 | foo: function(){ return 'foo'; }, 53 | protoOverride: 'circle' 54 | } 55 | }; 56 | Circle.willBeOverriden = 'circle'; 57 | Circle.prototype.foo = function(){ return 'foo'; }; 58 | Circle.prototype.protoOverride = 'circle'; 59 | 60 | var Extended = utils.extend(Shape, Circle); 61 | assert.equal(Extended.shapeProperty, 'shape'); 62 | assert.equal(Extended.willBeOverriden, 'circle'); 63 | 64 | var circle = new Extended(1, 2); 65 | assert(circle instanceof Extended); 66 | assert(circle instanceof Shape); 67 | 68 | assert.equal(circle.y, 2); 69 | assert.equal(circle.foo(), 'foo'); 70 | assert.equal(circle.protoOverride, 'circle'); 71 | 72 | done(); 73 | }); 74 | 75 | it('should extend classes from anonymous function', function(done) { 76 | var Extended = utils.extend(Shape, function () { 77 | Shape.apply(this, arguments); 78 | this.z = arguments[0]; 79 | }); 80 | assert.equal(Extended.shapeProperty, 'shape'); 81 | 82 | var circle = new Extended(1, 2); 83 | assert(circle instanceof Extended); 84 | assert(circle instanceof Shape); 85 | assert.equal(circle.y, 2); 86 | assert.equal(circle.z, 1); 87 | done(); 88 | }); 89 | 90 | 91 | it('should extend classes from function', function(done) { 92 | function Circle() { 93 | Shape.apply(this, arguments); 94 | this.z = arguments[0]; 95 | } 96 | Circle.willBeOverriden = 'circle'; 97 | Circle.prototype.foo = function(){ return 'foo'; }; 98 | Circle.prototype.protoOverride = 'circle'; 99 | Circle.prototype.overrideMethod = function(){ 100 | return this.$super.prototype.overrideMethod.call(this) + ' is Circle'; 101 | }; 102 | 103 | var Extended = utils.extend(Shape, Circle); 104 | assert.equal(Extended.shapeProperty, 'shape'); 105 | assert.equal(Extended.willBeOverriden, 'circle'); 106 | 107 | var circle = new Extended(1, 2); 108 | assert(circle instanceof Extended); 109 | assert(circle instanceof Shape); 110 | 111 | assert.equal(circle.y, 2); 112 | assert.equal(circle.z, 1); 113 | assert.equal(circle.foo(), 'foo'); 114 | assert.equal(circle.protoOverride, 'circle'); 115 | assert.equal(circle.overrideMethod(), 'shape is Circle'); 116 | 117 | done(); 118 | }); 119 | 120 | }); 121 | 122 | }); 123 | -------------------------------------------------------------------------------- /test/integration/runner.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Run integration tests 3 | * 4 | * Uses the `waterline-adapter-tests` module to 5 | * run mocha tests against the appropriate version 6 | * of Waterline. Only the interfaces explicitly 7 | * declared in this adapter's `package.json` file 8 | * are tested. (e.g. `queryable`, `semantic`, etc.) 9 | */ 10 | 11 | 12 | /** 13 | * Module dependencies 14 | */ 15 | 16 | var util = require('util'); 17 | var mocha = require('mocha'); 18 | var log = require('debug-logger')('sails-orientdb:test'); 19 | var TestRunner = require('waterline-adapter-tests'); 20 | var Adapter = require('../../'); 21 | 22 | var argvDatabaseType; 23 | if(process.argv.length > 2){ 24 | argvDatabaseType = process.argv[2]; 25 | } 26 | 27 | var config = require('../test-connection.json'); 28 | config.database = 'waterline-test-integration'; // We need different DB's due to https://github.com/orientechnologies/orientdb/issues/3301 29 | config.options.databaseType = argvDatabaseType || process.env.DATABASE_TYPE || config.options.databaseType || Adapter.defaults.options.databaseType; 30 | config.schema = process.env.SCHEMA !== undefined ? parseInt(process.env.SCHEMA) : Adapter.defaults.schema; 31 | 32 | 33 | // Grab targeted interfaces from this adapter's `package.json` file: 34 | var package = {}; 35 | var interfaces = []; 36 | try { 37 | package = require('../../package.json'); 38 | interfaces = package['waterlineAdapter'].interfaces; 39 | } 40 | catch (e) { 41 | throw new Error( 42 | '\n'+ 43 | 'Could not read supported interfaces from `waterlineAdapter.interfaces`'+'\n' + 44 | 'in this adapter\'s `package.json` file ::' + '\n' + 45 | util.inspect(e) 46 | ); 47 | } 48 | 49 | 50 | log.info('Testing `' + package.name + '`, a Sails/Waterline adapter.'); 51 | log.info('Running `waterline-adapter-tests` against ' + interfaces.length + ' interfaces...'); 52 | log.info('( ' + interfaces.join(', ') + ' )'); 53 | log.info('With database type: ' + config.options.databaseType); 54 | log.info('With schema: ' + !!config.schema); 55 | console.log(); 56 | log.info('Latest draft of Waterline adapter interface spec:'); 57 | log.info('https://github.com/balderdashy/sails-docs/blob/master/contributing/adapter-specification.md'); 58 | console.log(); 59 | 60 | 61 | 62 | 63 | /** 64 | * Integration Test Runner 65 | * 66 | * Uses the `waterline-adapter-tests` module to 67 | * run mocha tests against the specified interfaces 68 | * of the currently-implemented Waterline adapter API. 69 | */ 70 | new TestRunner({ 71 | 72 | // Load the adapter module. 73 | adapter: Adapter, 74 | 75 | // Default adapter config to use. 76 | config: config, 77 | 78 | // The set of adapter interfaces to test against. 79 | // (grabbed these from this adapter's package.json file above) 80 | interfaces: interfaces, 81 | 82 | // Mocha options 83 | // reference: https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically 84 | mocha: { 85 | timeout: 25000, 86 | reporter: 'spec', 87 | //grep: 'should return model instances' 88 | }, 89 | 90 | mochaChainableMethods: { 91 | //invert: true 92 | }, 93 | 94 | // Return code -1 if any test failed 95 | failOnError: true 96 | 97 | // Most databases implement 'semantic' and 'queryable'. 98 | // 99 | // As of Sails/Waterline v0.10, the 'associations' interface 100 | // is also available. If you don't implement 'associations', 101 | // it will be polyfilled for you by Waterline core. The core 102 | // implementation will always be used for cross-adapter / cross-connection 103 | // joins. 104 | // 105 | // In future versions of Sails/Waterline, 'queryable' may be also 106 | // be polyfilled by core. 107 | // 108 | // These polyfilled implementations can usually be further optimized at the 109 | // adapter level, since most databases provide optimizations for internal 110 | // operations. 111 | // 112 | // Full interface reference: 113 | // https://github.com/balderdashy/sails-docs/blob/master/contributing/adapter-specification.md 114 | }); 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /ci/orientdb-server-config-1.7.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /test/integration-orientdb/tests/define/schemalessProperties.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | _ = require('lodash'), 3 | Oriento = require('orientjs'); 4 | 5 | describe('Define related Operations', function() { 6 | 7 | describe('Property creation schemaless', function() { 8 | 9 | ///////////////////////////////////////////////////// 10 | // TEST SETUP 11 | //////////////////////////////////////////////////// 12 | 13 | var klass; 14 | 15 | before(function(done) { 16 | Associations.Schemaless_properties.native(function(nativeClass) { 17 | klass = nativeClass; 18 | 19 | Associations.Schemaless_properties 20 | .create({ 21 | schemaProp: 'schemaProp', 22 | customColumnProp: 'customColumnProp', 23 | schemaless: 'schemaless' 24 | }).exec(function(err, values){ 25 | if(err) { return done(err); } 26 | 27 | Associations.Properties.create({ 28 | stringProp: 'stringProp', 29 | textProp: 'textProp', 30 | propRequired: 'propRequired' 31 | }).exec(function(err, props){ 32 | if(err) { return done(err); } 33 | 34 | assert.equal(props.textProp, 'textProp'); 35 | done(); 36 | }); 37 | }); 38 | }); 39 | }); 40 | 41 | 42 | ///////////////////////////////////////////////////// 43 | // TEST METHODS 44 | //////////////////////////////////////////////////// 45 | 46 | it('should properly create string property from string', function(done) { 47 | klass.property.get('schemaProp') 48 | .then(function(property) { 49 | assert.equal(property.name, 'schemaProp'); 50 | assert.equal(Oriento.types[property.type], 'String'); 51 | done(); 52 | }) 53 | .error(done); 54 | }); 55 | 56 | it('should properly create property with custom column name', function(done) { 57 | klass.property.get('customCol') 58 | .then(function(property) { 59 | assert.equal(property.name, 'customCol'); 60 | assert.equal(Oriento.types[property.type], 'String'); 61 | done(); 62 | }) 63 | .error(done); 64 | }); 65 | 66 | it('should properly create Link property from model', function(done) { 67 | klass.property.get('modelProp') 68 | .then(function(property) { 69 | assert.equal(Oriento.types[property.type], 'Link'); 70 | assert.equal(property.linkedClass, 'indexesTable'); 71 | done(); 72 | }) 73 | .error(done); 74 | }); 75 | 76 | it('should return schemaless properties', function(done) { 77 | Associations.Schemaless_properties.findOne({ schemaProp: 'schemaProp' }).exec(function(err, record){ 78 | if(err) { return done(err); } 79 | 80 | assert.equal(record.schemaProp, 'schemaProp'); 81 | assert.equal(record.customColumnProp, 'customColumnProp'); 82 | assert.equal(record.schemaless, 'schemaless'); 83 | done(); 84 | }); 85 | }); 86 | 87 | it('should not return schemaless properties with select query', function(done) { 88 | Associations.Schemaless_properties.findOne({ select: ['customColumnProp'], where: { schemaProp: 'schemaProp' } }) 89 | .exec(function(err, record){ 90 | if(err) { return done(err); } 91 | 92 | assert.equal(record.schemaProp, undefined); 93 | assert.equal(record.customColumnProp, 'customColumnProp'); 94 | assert.equal(record.schemaless, undefined); 95 | done(); 96 | }); 97 | }); 98 | 99 | it('schemaful regression test: should not return properties ommitted in projection', function(done) { 100 | Associations.Properties.findOne({ select: ['stringProp'], where: { stringProp: 'stringProp' } }) 101 | .exec(function(err, record){ 102 | if(err) { return done(err); } 103 | 104 | assert.equal(record.stringProp, 'stringProp'); 105 | assert.equal(record.textProp, undefined); 106 | assert.equal(record.propRequired, undefined); 107 | done(); 108 | }); 109 | }); 110 | 111 | 112 | }); 113 | }); -------------------------------------------------------------------------------- /test/integration-orientdb/bugs/47-schema_with_id.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | _ = require('lodash'), 3 | utils = require('../../../lib/utils'); 4 | 5 | var self = this; 6 | 7 | describe('Bug #47: Schema with id (blueprints like)', function() { 8 | before(function (done) { 9 | 10 | var fixtures = { 11 | UserFixture : { 12 | identity : 'user', 13 | 14 | attributes : { 15 | username : { 16 | type : 'string' 17 | }, 18 | email : { 19 | type : 'email' 20 | }, 21 | } 22 | }, 23 | PassportFixture : { 24 | identity : 'passport', 25 | 26 | attributes : { 27 | id : { 28 | type : 'string', 29 | primaryKey : true, 30 | columnName : '@rid' 31 | }, 32 | 33 | password : { 34 | type : 'string' 35 | } 36 | } 37 | } 38 | }; 39 | 40 | CREATE_TEST_WATERLINE(self, 'test_bug_47', fixtures, done); 41 | }); 42 | after(function (done) { 43 | DELETE_TEST_WATERLINE('test_bug_47', done); 44 | }); 45 | 46 | describe('create user', function() { 47 | 48 | ///////////////////////////////////////////////////// 49 | // TEST SETUP 50 | //////////////////////////////////////////////////// 51 | 52 | var userRecord, passportRecord, passportNullIdRecord; 53 | 54 | before(function (done) { 55 | self.collections.User.create({ email: 'user1@example.com' }, function(err, user) { 56 | if(err) { return done(err); } 57 | userRecord = user; 58 | 59 | self.collections.Passport.create({ password: 'passport1' }, function(err, passport) { 60 | if(err) { return done(err); } 61 | passportRecord = passport; 62 | 63 | self.collections.Passport.create({ password: 'passport2', id: null }, function(err, passport2) { 64 | if(err) { return done(err); } 65 | passportNullIdRecord = passport2; 66 | done(); 67 | }); 68 | 69 | }); 70 | }); 71 | }); 72 | 73 | 74 | ///////////////////////////////////////////////////// 75 | // TEST METHODS 76 | //////////////////////////////////////////////////// 77 | 78 | it('should be robust against an insertion with id set', function(done) { 79 | // we probably should throw an error... 80 | self.collections.User.create({ email: 'email@example.com', id: '#13:1' }, function(err, user) { 81 | if(err) { return done(err); } 82 | assert.equal(user.email, 'email@example.com'); 83 | done(); 84 | 85 | }); 86 | }); 87 | 88 | it('regression test: should retrieve user by id', function(done) { 89 | self.collections.User.findOne(userRecord.id, function(err, user) { 90 | if(err) { return done(err); } 91 | assert.equal(user.email, 'user1@example.com'); 92 | done(); 93 | }); 94 | }); 95 | 96 | it('should be robust against an insertion with a null id', function(done) { 97 | self.collections.Passport.create({ password: 'blah', id: null }, function foundUser(err, passport) { 98 | if(err) { return done(err); } 99 | assert.equal(passport.password, 'blah'); 100 | done(); 101 | }); 102 | }); 103 | 104 | it('should be robust against an insertion with id set', function(done) { 105 | // we probably should throw an error... 106 | self.collections.Passport.create({ password: 'blah', id: '#13:1' }, function(err, passport) { 107 | if(err) { return done(err); } 108 | assert.equal(passport.password, 'blah'); 109 | done(); 110 | }); 111 | }); 112 | 113 | it('regression test: should retrieve passport by id', function(done) { 114 | self.collections.Passport.findOne(passportRecord.id, function foundPassport(err, passport) { 115 | if(err) { return done(err); } 116 | assert.equal(passport.password, 'passport1'); 117 | done(); 118 | }); 119 | }); 120 | 121 | it('regression test: should retrieve passport by id even if submitted with id `null`', function(done) { 122 | self.collections.Passport.findOne(passportNullIdRecord.id, function foundPassport(err, passport2) { 123 | if(err) { return done(err); } 124 | assert.equal(passport2.password, 'passport2'); 125 | done(); 126 | }); 127 | }); 128 | 129 | 130 | }); 131 | }); 132 | -------------------------------------------------------------------------------- /test/integration-orientdb/tests/associations/manyThroughSelf.add.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | _ = require('lodash'); 3 | 4 | describe('Association Interface', function() { 5 | 6 | describe('Has Many Through Association', function() { 7 | 8 | ///////////////////////////////////////////////////// 9 | // TEST SETUP 10 | //////////////////////////////////////////////////// 11 | 12 | var friendRecord, followeeRecord, followerRecord; 13 | 14 | before(function(done) { 15 | Associations.Friend.create({ name: 'hasManyThrough friend'}, function(err, friend) { 16 | if(err) return done(err); 17 | friendRecord = friend; 18 | Associations.Friend.create({ name: 'hasManyThrough followee' }, function(err, followee) { 19 | if(err) return done(err); 20 | followeeRecord = followee; 21 | Associations.Friend.create({ name: 'hasManyThrough follower' }, function(err, follower) { 22 | if(err) return done(err); 23 | followerRecord = follower; 24 | done(); 25 | }); 26 | }); 27 | }); 28 | }); 29 | 30 | describe('.add', function() { 31 | 32 | ///////////////////////////////////////////////////// 33 | // TEST METHODS 34 | //////////////////////////////////////////////////// 35 | 36 | it('should link a friend to a followee through a join table', function(done) { 37 | friendRecord.followees.add(followeeRecord.id); 38 | friendRecord.save(function(err){ 39 | assert(!err, err); 40 | 41 | Associations.Friend.findOne(friendRecord.id) 42 | .populate('followees') 43 | .exec(function(err, friend) { 44 | assert(!err, err); 45 | 46 | assert(Array.isArray(friend.followees)); 47 | assert(friend.followees.length === 1, 'No followees found'); 48 | assert(friend.followees[0].name === 'hasManyThrough followee'); 49 | 50 | Associations.Friend.findOne({'out().@rid': [followeeRecord.id] }) 51 | .exec(function(err, friendWhoFollows) { 52 | assert(!err, err); 53 | 54 | assert(friendWhoFollows, "Edge has wrong direction"); 55 | assert(friendWhoFollows.name === 'hasManyThrough friend', "Edge has wrong direction"); 56 | done(); 57 | }); 58 | 59 | }); 60 | }); 61 | }); 62 | 63 | it('should populate a followee with followers through a join table', function(done) { 64 | //TODO: depends on previous test, add EventEmitter2's mediator so tests can be ran async safely 65 | Associations.Friend.findOne(followeeRecord.id) 66 | .populate('followers') 67 | .exec(function(err, followed) { 68 | assert(!err, err); 69 | 70 | assert(Array.isArray(followed.followers)); 71 | assert(followed.followers.length === 1, 'actual length: ' + followed.followers.length); 72 | assert(followed.followers[0].name === 'hasManyThrough friend'); 73 | 74 | done(); 75 | }); 76 | }); 77 | 78 | xit('should link a friend to a follower through a join table', function(done) { 79 | friendRecord.followers.add(followerRecord.id); 80 | friendRecord.save(function(err){ 81 | assert(!err, err); 82 | 83 | Associations.Friend.findOne(followerRecord.id) 84 | .populate('followees') 85 | .exec(function(err, follower) { 86 | assert(!err, err); 87 | 88 | assert(Array.isArray(follower.followees)); 89 | assert(follower.followees.length === 1, 'actual length: ' + follower.followees.length); 90 | assert(follower.followees[0].name === 'hasManyThrough friend'); 91 | 92 | done(); 93 | }); 94 | }); 95 | }); 96 | 97 | xit('should populate a friend with followers through a join table', function(done) { 98 | //TODO: depends on previous test, add EventEmitter2's mediator so tests can be ran async safely 99 | Associations.Friend.findOne(friendRecord.id) 100 | .populate('followers') 101 | .exec(function(err, friend) { 102 | assert(!err, err); 103 | 104 | assert(Array.isArray(friend.followers)); 105 | assert(friend.followers.length === 1, 'actual length: ' + friend.followers.length); 106 | assert(friend.followers[0].name === 'hasManyThrough follower'); 107 | 108 | done(); 109 | }); 110 | }); 111 | 112 | }); 113 | }); 114 | }); 115 | -------------------------------------------------------------------------------- /test/integration-orientdb/tests/associations/manyToMany.selfReferencing.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | 3 | var self = this, 4 | fixtures; 5 | 6 | describe('Association Interface', function() { 7 | 8 | describe('n:m association :: self-reference', function() { 9 | 10 | ///////////////////////////////////////////////////// 11 | // TEST SETUP 12 | //////////////////////////////////////////////////// 13 | 14 | before(function (done) { 15 | 16 | fixtures = { 17 | UserFixture : { 18 | identity : 'user', 19 | attributes : { 20 | name : 'string', 21 | follows : { 22 | collection : 'user', 23 | via : 'followedBy', 24 | dominant: true 25 | }, 26 | followedBy : { 27 | collection : 'user', 28 | via : 'follows' 29 | }, 30 | relationships : function(){ 31 | if(!this.follows) { return this.followedBy || []; }; 32 | if(!this.followedBy) { return this.follows || []; }; 33 | return this.follows.concat(this.followedBy); 34 | } 35 | } 36 | } 37 | }; 38 | 39 | CREATE_TEST_WATERLINE(self, 'test_mn_slreferencing', fixtures, done); 40 | }); 41 | after(function (done) { 42 | DELETE_TEST_WATERLINE('test_mn_slreferencing', done); 43 | }); 44 | 45 | describe('userA --follows--> userB --follows--> userC', function() { 46 | 47 | var userA, userB, userC; 48 | 49 | before(function (done) { 50 | self.collections.User.create([{ name: 'userA' }, { name: 'userB' }, { name: 'userC' }]) 51 | .then(function(users){ 52 | userA = users[0]; 53 | userB = users[1]; 54 | userC = users[2]; 55 | 56 | userA.follows.add(userB); 57 | return userA.save(); 58 | }) 59 | .then(function(){ 60 | userB.follows.add(userC); 61 | return userB.save(); 62 | // userA --follows--> userB --follows--> userC 63 | // userC --followedBy--> userB --followedBy--> userA 64 | }) 65 | .then(function(){ done(); }) 66 | .catch(done); 67 | }); 68 | 69 | ///////////////////////////////////////////////////// 70 | // TEST METHODS 71 | //////////////////////////////////////////////////// 72 | 73 | it('userA should follow userB and not be followed by anyone', function(done) { 74 | self.collections.User.findOne(userA.id) 75 | .populate('follows') 76 | .populate('followedBy') 77 | .then(function(user){ 78 | assert.equal(user.name, 'userA'); 79 | assert.equal(user.follows.length, 1); 80 | assert.equal(user.follows[0].name, 'userB'); 81 | assert.equal(user.followedBy.length, 0); 82 | assert.equal(user.relationships().length, 1); 83 | assert.equal(user.relationships()[0].name, 'userB'); 84 | done(); 85 | }) 86 | .catch(done); 87 | }); 88 | 89 | it('userB should follow userC and be followed by userA', function(done) { 90 | self.collections.User.findOne(userB.id) 91 | .populate('follows') 92 | .populate('followedBy') 93 | .then(function(user){ 94 | assert.equal(user.name, 'userB'); 95 | assert.equal(user.follows.length, 1); 96 | assert.equal(user.follows[0].name, 'userC'); 97 | assert.equal(user.followedBy.length, 1); 98 | assert.equal(user.followedBy[0].name, 'userA'); 99 | assert.equal(user.relationships().length, 2); 100 | assert.equal(user.relationships()[0].name, 'userC'); 101 | assert.equal(user.relationships()[1].name, 'userA'); 102 | done(); 103 | }) 104 | .catch(done); 105 | }); 106 | 107 | it('userC should not follow anyone and be followed by userB', function(done) { 108 | self.collections.User.findOne(userC.id) 109 | .populate('follows') 110 | .populate('followedBy') 111 | .then(function(user){ 112 | assert.equal(user.name, 'userC'); 113 | assert.equal(user.follows.length, 0); 114 | assert.equal(user.followedBy.length, 1); 115 | assert.equal(user.followedBy[0].name, 'userB'); 116 | assert.equal(user.relationships().length, 1); 117 | assert.equal(user.relationships()[0].name, 'userB'); 118 | done(); 119 | }) 120 | .catch(done); 121 | }); 122 | 123 | }); 124 | }); 125 | }); 126 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to sails-orientdb 2 | 3 | 1. [Getting Involved](#getting-involved) 4 | 2. [Discussion](#discussion) 5 | 3. [How To Report Bugs](#how-to-report-bugs) 6 | 4. [Tips For Submitting Code](#tips-for-submitting-code) 7 | 8 | ## Getting Involved 9 | 10 | There are a number of ways to get involved with the development of sails-orientdb. Even if you've never contributed code to an Open Source project before, we're always looking for help identifying bugs, cleaning up code, writing documentation and testing. 11 | 12 | The goal of this guide is to provide the best way to contribute to sails-orientdb. 13 | 14 | ## Discussion 15 | 16 | ### Gitter.im 17 | [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/appscot/sails-orientdb?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 18 | 19 | We frequently tracks posts on [Gitter](https://gitter.im/appscot/sails-orientdb). If you have longer posts or questions please feel free to post them there. If you think you've found a bug please [file it in the bug tracker](#how-to-report-bugs). 20 | 21 | 22 | ## How to Report Bugs 23 | 24 | ### Try the latest version of sails-orientdb 25 | 26 | Bugs in old versions of sails-orientdb may have already been fixed. In order to avoid reporting known issues, make sure you are always testing against the latest build/source. Please follow these guidelines before reporting a bug: 27 | 28 | 1. **Update to the latest version** — Check if you can reproduce the issue with the latest version from the `master` branch. You can install from `master` by running: 29 | ``` sh 30 | npm install appscot/sails-orientdb 31 | ``` 32 | 33 | 2. **Enable logging** — `sails-orientdb` uses [debug-logger](https://github.com/appscot/debug-logger) (a wrapper around [visionmedia/debug](https://github.com/visionmedia/debug)) and so you can enable logging by running the below command: 34 | ``` sh 35 | export DEBUG=$DEBUG,sails-orientdb:* 36 | ``` 37 | 38 | 3. **Use the Issues search** — check if the issue has already been reported. If it has been, please comment on the existing issue. 39 | 40 | 4. **Provide a means to reproduce the problem** — Please provide as much details as possible, e.g. `sails-orientdb` logs, `sails-orientdb`, `OrientDB` and `waterline` versions, and of course the steps to reproduce the problem. Ideally, submit an automated test such as [47-schema_with_id.js](https://github.com/appscot/sails-orientdb/blob/master/test/integration-orientdb/bugs/47-schema_with_id.js). 41 | 42 | ### Report a bug 43 | 44 | Fill in a bug by creating a [new github issue](https://github.com/appscot/sails-orientdb/issues/new) and provide as much information as possible. 45 | 46 | ### Feature requests 47 | 48 | Please follow the bug guidelines above for feature requests, i.e. update to the latest version and search for existing issues before posting a new request. 49 | 50 | 51 | ## Tips For Submitting Code 52 | 53 | 54 | ### Code 55 | 56 | **NEVER write your patches to the master branch** - it gets messy (I say this from experience!) 57 | 58 | **ALWAYS USE A "TOPIC" BRANCH!** Personally I like the `issuenumber-feature_name` format that way its easy to identify the branch and feature at a glance. Also please make note of any issue number in the pull commit so we know what you are solving (it helps with cleaning up the related items later). 59 | 60 | 61 | ### Running The Tests 62 | 63 | The tests are based on [mocha](http://visionmedia.github.io/mocha) and [commonjs-assert](https://github.com/defunctzombie/commonjs-assert). 64 | 65 | Before running the tests, ensure you've configured your orientdb server to use the same credentials as in [test-connection.json](./test/test-connection.json). 66 | 67 | To run the standard tests: 68 | ```sh 69 | npm test 70 | ``` 71 | 72 | To run all tests (including document DB and schemaless mode): 73 | ```sh 74 | make test-all 75 | ``` 76 | 77 | To generate the code coverage report, run: 78 | ```sh 79 | npm run coverage 80 | ``` 81 | And have a look at `coverage/lcov-report/index.html`. 82 | 83 | 84 | ### Pull requests 85 | 86 | [Pull requests](https://help.github.com/articles/using-pull-requests) are welcome and the preferred way of accepting code contributions. 87 | 88 | Please follow these guidelines before sending a pull request: 89 | 90 | 1. Update your fork to the latest upstream version. 91 | 2. Use the `master` branch to base your code off of. 92 | 3. Follow the coding conventions of the original repository. Do not change line endings of the existing file, as this will rewrite the file and loses history. 93 | 4. Keep your commits as autonomous as possible, i.e. create a new commit for every single bug fix or feature added. 94 | 5. Always add meaningful commit messages. We should not have to guess at what your code is suppose to do. 95 | 6. Make sure your changes pass all automated tests (old and new). 96 | -------------------------------------------------------------------------------- /test/integration-orientdb/tests/config/options.test..js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | _ = require('lodash'); 3 | 4 | var self = this, 5 | fixtures, 6 | config; 7 | 8 | describe('Config tests', function() { 9 | before(function (done) { 10 | 11 | fixtures = { 12 | UserFixture : { 13 | identity : 'user', 14 | attributes : { 15 | name : 'string' 16 | } 17 | }, 18 | ThingFixture : { 19 | identity : 'thing', 20 | 21 | attributes : { 22 | name : 'string' 23 | } 24 | } 25 | }; 26 | 27 | config = { 28 | user : 'root', 29 | password : 'root', 30 | database: 'test_config_options_db' 31 | }; 32 | 33 | CREATE_TEST_WATERLINE(self, config, fixtures, done); 34 | }); 35 | after(function (done) { 36 | DELETE_TEST_WATERLINE(config, done); 37 | }); 38 | 39 | describe('options', function() { 40 | 41 | describe('pool', function() { 42 | 43 | ///////////////////////////////////////////////////// 44 | // TEST SETUP 45 | //////////////////////////////////////////////////// 46 | 47 | before(function (done) { 48 | // db created, let's close the connection so we can test logins 49 | self.waterline.teardown(done); 50 | }); 51 | 52 | after(function (done) { 53 | // let's log off last user because it may not have privileges to drop the db later on 54 | self.waterline.teardown(function (err) { 55 | if (err) {  return done(err); } 56 | // and now we logon with original config 57 | CREATE_TEST_WATERLINE(self, config, fixtures, done); 58 | }); 59 | }); 60 | 61 | ///////////////////////////////////////////////////// 62 | // TEST METHODS 63 | //////////////////////////////////////////////////// 64 | 65 | it('should be undefined', function (done) { 66 | CREATE_TEST_WATERLINE(self, config, fixtures, function (err) { 67 | if (err) {  return done(err); } 68 | var server = self.collections.User.getServer(); 69 | assert.equal(server.transport.pool, undefined); 70 | done(); 71 | }); 72 | }); 73 | 74 | it('should have max equal 10', function (done) { 75 | self.waterline.teardown(function (err) { 76 | if (err) {  return done(err); } 77 | 78 | var newConfig = _.cloneDeep(config); 79 | 80 | newConfig.options = { 81 | pool: { max: 10 } 82 | }; 83 | 84 | CREATE_TEST_WATERLINE(self, newConfig, fixtures, function (err) { 85 | if (err) {  return done(err); } 86 | var server = self.collections.User.getServer(); 87 | assert.equal(server.transport.pool.max, 10); 88 | done(); 89 | }); 90 | }); 91 | }); 92 | 93 | }); 94 | 95 | 96 | describe('useToken', function() { 97 | 98 | ///////////////////////////////////////////////////// 99 | // TEST SETUP 100 | //////////////////////////////////////////////////// 101 | 102 | before(function (done) { 103 | // db created, let's close the connection so we can test logins 104 | self.waterline.teardown(done); 105 | }); 106 | 107 | after(function (done) { 108 | // let's log off last user because it may not have privileges to drop the db later on 109 | self.waterline.teardown(function (err) { 110 | if (err) {  return done(err); } 111 | // and now we logon with original config 112 | CREATE_TEST_WATERLINE(self, config, fixtures, done); 113 | }); 114 | }); 115 | 116 | ///////////////////////////////////////////////////// 117 | // TEST METHODS 118 | //////////////////////////////////////////////////// 119 | 120 | it('should be false', function (done) { 121 | CREATE_TEST_WATERLINE(self, config, fixtures, function (err) { 122 | if (err) {  return done(err); } 123 | var server = self.collections.User.getServer(); 124 | assert.equal(server.transport.useToken, false); 125 | done(); 126 | }); 127 | }); 128 | 129 | it('should be true', function (done) { 130 | self.waterline.teardown(function (err) { 131 | if (err) {  return done(err); } 132 | 133 | var newConfig = _.cloneDeep(config); 134 | 135 | newConfig.options = { 136 | useToken: true 137 | }; 138 | 139 | CREATE_TEST_WATERLINE(self, newConfig, fixtures, function (err) { 140 | if (err) {  return done(err); } 141 | var server = self.collections.User.getServer(); 142 | assert.equal(server.transport.useToken, true); 143 | done(); 144 | }); 145 | }); 146 | }); 147 | 148 | }); 149 | 150 | 151 | }); 152 | }); 153 | -------------------------------------------------------------------------------- /example/express/express-example.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A simple example of how to use Waterline v0.10 with Express 3 | */ 4 | 5 | ////////////////////////////////////////////////////////////////// 6 | // Install dependencies: 7 | // npm install waterline 8 | // npm install sails-orientdb 9 | // npm install express 10 | // npm install body-parser 11 | // npm install method-override 12 | ////////////////////////////////////////////////////////////////// 13 | 14 | 15 | var express = require('express'), 16 | app = express(), 17 | Waterline = require('waterline'), 18 | bodyParser = require('body-parser'), 19 | methodOverride = require('method-override'); 20 | 21 | 22 | 23 | 24 | // Instantiate a new instance of the ORM 25 | var orm = new Waterline(); 26 | 27 | 28 | ////////////////////////////////////////////////////////////////// 29 | // WATERLINE CONFIG 30 | ////////////////////////////////////////////////////////////////// 31 | 32 | // Require any waterline compatible adapters here 33 | var orientAdapter = require('sails-orientdb'); 34 | 35 | 36 | // Build A Config Object 37 | var config = { 38 | 39 | // Setup Adapters 40 | // Creates named adapters that have have been required 41 | adapters: { 42 | 'default': orientAdapter, 43 | orient: orientAdapter, 44 | }, 45 | 46 | // Build Connections Config 47 | // Setup connections using the named adapter configs 48 | connections: { 49 | myLocalOrient: { 50 | adapter: 'orient', 51 | host: 'localhost', 52 | port: 2424, 53 | user: 'root', 54 | password: 'root', 55 | database: 'waterline-express' 56 | } 57 | }, 58 | 59 | defaults: { 60 | migrate: 'alter' 61 | } 62 | 63 | }; 64 | 65 | 66 | ////////////////////////////////////////////////////////////////// 67 | // WATERLINE MODELS 68 | ////////////////////////////////////////////////////////////////// 69 | 70 | var User = Waterline.Collection.extend({ 71 | 72 | identity: 'user', 73 | connection: 'myLocalOrient', 74 | 75 | attributes: { 76 | first_name: 'string', 77 | last_name: 'string' 78 | } 79 | }); 80 | 81 | var Pet = Waterline.Collection.extend({ 82 | 83 | identity: 'pet', 84 | connection: 'myLocalOrient', 85 | 86 | attributes: { 87 | name: 'string', 88 | breed: 'string' 89 | } 90 | }); 91 | 92 | 93 | // Load the Models into the ORM 94 | orm.loadCollection(User); 95 | orm.loadCollection(Pet); 96 | 97 | 98 | 99 | ////////////////////////////////////////////////////////////////// 100 | // EXPRESS SETUP 101 | ////////////////////////////////////////////////////////////////// 102 | 103 | 104 | // Setup Express Application 105 | app.use(bodyParser.urlencoded({ extended: false })); 106 | app.use(bodyParser.json()); 107 | app.use(methodOverride()); 108 | 109 | // Build Express Routes (CRUD routes for /users) 110 | 111 | app.get('/users', function(req, res) { 112 | app.models.user.find().exec(function(err, models) { 113 | if(err) return res.json({ err: err }, 500); 114 | res.json(models); 115 | }); 116 | }); 117 | 118 | app.post('/users', function(req, res) { 119 | app.models.user.create(req.body, function(err, model) { 120 | if(err) return res.json({ err: err }, 500); 121 | res.json(model); 122 | }); 123 | }); 124 | 125 | app.get('/users/:id', function(req, res) { 126 | app.models.user.findOne({ id: req.params.id }, function(err, model) { 127 | if(err) return res.json({ err: err }, 500); 128 | res.json(model); 129 | }); 130 | }); 131 | 132 | app.delete('/users/:id', function(req, res) { 133 | app.models.user.destroy({ id: req.params.id }, function(err) { 134 | if(err) return res.json({ err: err }, 500); 135 | res.json({ status: 'ok' }); 136 | }); 137 | }); 138 | 139 | app.put('/users/:id', function(req, res) { 140 | // Don't pass ID to update 141 | delete req.body.id; 142 | 143 | app.models.user.update({ id: req.params.id }, req.body, function(err, model) { 144 | if(err) return res.json({ err: err }, 500); 145 | res.json(model); 146 | }); 147 | }); 148 | 149 | 150 | 151 | ////////////////////////////////////////////////////////////////// 152 | // START WATERLINE 153 | ////////////////////////////////////////////////////////////////// 154 | 155 | // Start Waterline passing adapters in 156 | orm.initialize(config, function(err, models) { 157 | if(err) throw err; 158 | 159 | app.models = models.collections; 160 | app.connections = models.connections; 161 | 162 | // Start Server 163 | app.listen(3000); 164 | 165 | console.log(); 166 | console.log('To list saved users, visit http://localhost:3000/users'); 167 | console.log(); 168 | console.log('To retrieve a specific user, visit http://localhost:3000/users/'); 169 | console.log('Note: OrientDB ids need to be URI encoded, so #11:0 would be accessed at http://localhost:3000/users/%2311%3A0'); 170 | console.log('You can add users with curl, e.g.: curl --data "first_name=john&last_name=doe" http://localhost:3000/users'); 171 | }); 172 | 173 | -------------------------------------------------------------------------------- /test/integration-orientdb/bootstrap.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module Dependencies 3 | */ 4 | var Waterline = require('waterline'); 5 | var _ = require('lodash'); 6 | var async = require('async'); 7 | var Adapter = require('../../'); 8 | 9 | var config = require('../test-connection.json'); 10 | config.database = 'waterline-test-orientdb'; 11 | config.options = config.options || {}; 12 | config.options.storage = "memory"; 13 | 14 | 15 | // Require Fixtures 16 | var originalFixturesPath = '../../node_modules/waterline-adapter-tests/interfaces/associations/support/fixtures/'; 17 | 18 | var fixtures = { 19 | PaymentBelongsFixture: require(originalFixturesPath + 'belongsTo.child.fixture'), 20 | CustomerBelongsFixture: require(originalFixturesPath + 'belongsTo.parent.fixture'), 21 | PaymentHasManyFixture: require(originalFixturesPath + 'hasMany.child.fixture'), 22 | CustomerHasManyFixture: require(originalFixturesPath + 'hasMany.parent.fixture'), 23 | ApartmentHasManyFixture: require(originalFixturesPath + 'hasMany.customPK.fixture'), 24 | PaymentManyFixture: require(originalFixturesPath + 'multipleAssociations.fixture').payment, 25 | CustomerManyFixture: require(originalFixturesPath + 'multipleAssociations.fixture').customer, 26 | // StadiumFixture: require(originalFixturesPath + 'hasManyThrough.stadium.fixture'), 27 | StadiumFixture: require('./fixtures/hasManyThrough.stadium.fixture'), 28 | TeamFixture: require(originalFixturesPath + 'hasManyThrough.team.fixture'), 29 | //VenueFixture: require(originalFixturesPath + 'hasManyThrough.venue.fixture'), 30 | VenueFixture: require('./fixtures/hasManyThrough.venueHack.fixture'), 31 | TaxiFixture: require(originalFixturesPath + 'manyToMany.taxi.fixture'), 32 | //DriverFixture: require(originalFixturesPath + 'manyToMany.driver.fixture'), 33 | DriverFixture: require('./fixtures/manyToMany.driverHack.fixture.js'), 34 | UserOneFixture: require(originalFixturesPath + 'oneToOne.fixture').user_resource, 35 | ProfileOneFixture: require(originalFixturesPath + 'oneToOne.fixture').profile, 36 | 37 | FriendFixture: require('./fixtures/hasManyThrough.friend.fixture'), 38 | FollowsFixture: require('./fixtures/hasManyThrough.follows.fixture'), 39 | OwnsFixture: require('./fixtures/hasManyThrough.owns.fixture'), 40 | 41 | IndexesFixture: require('./fixtures/define.indexes.fixture'), 42 | PropertiesFixture: require('./fixtures/define.properties.fixture'), 43 | SchemalessPropertiesFixture: require('./fixtures/define.schemalessProperties.fixture'), 44 | CounterFixture: require('./fixtures/counter.fixture'), 45 | }; 46 | 47 | 48 | ///////////////////////////////////////////////////// 49 | // TEST SETUP 50 | //////////////////////////////////////////////////// 51 | 52 | var waterline, ontology; 53 | 54 | before(function(done) { 55 | this.timeout(60000); // to prevent travis from breaking the build 56 | 57 | //globals 58 | global.Associations = {}; 59 | 60 | waterline = new Waterline(); 61 | 62 | Object.keys(fixtures).forEach(function(key) { 63 | var fixture = fixtures[key]; 64 | if(typeof fixture !== 'function'){ 65 | // fixture definition has not been extended to collection yet 66 | fixture = Waterline.Collection.extend(fixture); 67 | } 68 | waterline.loadCollection(fixture); 69 | }); 70 | 71 | var Connections = { 72 | 'test': config 73 | }; 74 | Connections.test.adapter = 'wl_tests'; 75 | 76 | var connections = { associations: _.clone(Connections.test) }; 77 | 78 | var defaults = { migrate: 'alter' }; 79 | 80 | waterline.initialize({ adapters: { wl_tests: Adapter }, connections: connections, defaults: defaults }, function(err, _ontology) { 81 | if(err) { 82 | console.log('ERROR:', err); 83 | done(err); 84 | } 85 | 86 | ontology = _ontology; 87 | 88 | Object.keys(_ontology.collections).forEach(function(key) { 89 | var globalName = key.charAt(0).toUpperCase() + key.slice(1); 90 | global.Associations[globalName] = _ontology.collections[key]; 91 | }); 92 | 93 | done(); 94 | }); 95 | }); 96 | 97 | after(function(done) { 98 | 99 | function dropCollection(item, next) { 100 | if(!Adapter.hasOwnProperty('drop')) return next(); 101 | 102 | // TODO: this is causing OrientDB.ConnectionError [2]: write EPIPE 103 | // ontology.collections[item].drop(function(err) { 104 | // if(err) return next(err); 105 | next(); 106 | // }); 107 | } 108 | 109 | async.each(Object.keys(ontology.collections), dropCollection, function(err) { 110 | if(err) { 111 | console.log('ERROR:', err); 112 | done(err); 113 | } 114 | 115 | ontology.collections[Object.keys(ontology.collections)[0]].getServer(function(server){ 116 | server.drop({ 117 | name: config.database, 118 | storage: config.options.storage 119 | }) 120 | .then(function(err){ 121 | waterline.teardown(done); 122 | }) 123 | .catch(done); 124 | }); 125 | }); 126 | 127 | }); 128 | -------------------------------------------------------------------------------- /test/unit/collection.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Test dependencies 3 | */ 4 | var assert = require('assert'), 5 | Collection = require('../../lib/collection'), 6 | _ = require('lodash'); 7 | 8 | 9 | describe('collection class', function () { 10 | 11 | var defaultModel = { 12 | identity: 'default', 13 | attributes: { name: 'string' }, 14 | definition: { name: 'string' } 15 | }; 16 | 17 | var collections = {}; 18 | collections.defaultModel = _.defaults({ }, defaultModel); 19 | collections.documentModel1 = _.defaults({ orientdbClass: '' }, defaultModel); 20 | collections.documentModel2 = _.defaults({ orientdbClass: 'document' }, defaultModel); 21 | collections.vertexModel = _.defaults({ orientdbClass: 'V' }, defaultModel); 22 | collections.edgeModel = _.defaults({ orientdbClass: 'E' }, defaultModel); 23 | collections.junctionModelThrough = _.defaults({ junctionTable: true }, defaultModel); 24 | collections.junctionModelThroughD = _.defaults({ orientdbClass: '', junctionTable: true }, defaultModel); 25 | collections.junctionModelThroughV = _.defaults({ orientdbClass: 'V', junctionTable: true }, defaultModel); 26 | collections.junctionModel = _.defaults({ 27 | identity : 'driver_taxis__taxi_drivers', 28 | tableName : 'driver_taxis__taxi_drivers', 29 | junctionTable : true 30 | }, defaultModel); 31 | collections.junctionModelE = _.defaults({ 32 | orientdbClass: 'E', 33 | identity : 'driver_taxis__taxi_drivers', 34 | tableName : 'driver_taxis__taxi_drivers', 35 | junctionTable : true 36 | }, defaultModel); 37 | 38 | junctionTable: true, 39 | 40 | before(function(done){ 41 | done(); 42 | }); 43 | 44 | describe('document database', function () { 45 | 46 | var documentConnectionMock = { config: { options: { databaseType: 'document' } } }; 47 | 48 | it('constructor: should instantiate a document regardless of orientdbClass value', function (done) { 49 | _.values(collections).forEach(function(collection){ 50 | var doc = new Collection(collection, documentConnectionMock, null); 51 | assert(doc instanceof Collection.Document); 52 | assert(!(doc instanceof Collection.Vertex)); 53 | assert(!(doc instanceof Collection.Edge)); 54 | }); 55 | done(); 56 | }); 57 | }); 58 | 59 | describe('graph database', function () { 60 | 61 | var graphConnectionMock = { config: { options: { databaseType: 'graph' } } }; 62 | 63 | it('constructor: should instantiate a document if orientdbClass is "" or "document"', function (done) { 64 | var doc = new Collection(collections.documentModel1, graphConnectionMock, null); 65 | assert(doc instanceof Collection.Document); 66 | assert(!(doc instanceof Collection.Vertex)); 67 | assert(!(doc instanceof Collection.Edge)); 68 | doc = new Collection(collections.documentModel2, graphConnectionMock, null); 69 | assert(doc instanceof Collection.Document); 70 | assert(!(doc instanceof Collection.Vertex)); 71 | assert(!(doc instanceof Collection.Edge)); 72 | doc = new Collection(collections.junctionModelThroughD, graphConnectionMock, null); 73 | assert(doc instanceof Collection.Document); 74 | assert(!(doc instanceof Collection.Vertex)); 75 | assert(!(doc instanceof Collection.Edge)); 76 | 77 | done(); 78 | }); 79 | 80 | it('constructor: should instantiate a vertex if orientdbClass is undefined or "V"', function (done) { 81 | var vertex = new Collection(collections.defaultModel, graphConnectionMock, null); 82 | assert(vertex instanceof Collection.Document); 83 | assert(vertex instanceof Collection.Vertex); 84 | vertex = new Collection(collections.vertexModel, graphConnectionMock, null); 85 | assert(vertex instanceof Collection.Vertex); 86 | vertex = new Collection(collections.junctionModelThroughV, graphConnectionMock, null); 87 | assert(vertex instanceof Collection.Vertex); 88 | 89 | done(); 90 | }); 91 | 92 | it('constructor: should instantiate an edge if orientdbClass is "E"', function (done) { 93 | var edge = new Collection(collections.edgeModel, graphConnectionMock, null); 94 | assert(edge instanceof Collection.Edge); 95 | edge = new Collection(collections.junctionModelE, graphConnectionMock, null); 96 | assert(edge instanceof Collection.Edge); 97 | 98 | done(); 99 | }); 100 | 101 | it('constructor: should instantiate an edge if table is junction table for a many-to-many association', function (done) { 102 | var edge = new Collection(collections.junctionModel, graphConnectionMock, null); 103 | assert(edge instanceof Collection.Edge); 104 | done(); 105 | }); 106 | 107 | it('constructor: should instantiate an edge if table is junction table for a many-to-many through association', function (done) { 108 | var edge = new Collection(collections.junctionModelThrough, graphConnectionMock, null); 109 | assert(edge instanceof Collection.Edge); 110 | done(); 111 | }); 112 | 113 | }); 114 | 115 | 116 | }); 117 | -------------------------------------------------------------------------------- /test/unit/utils.rewriteIds.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Test dependencies 3 | */ 4 | var assert = require('assert'), 5 | utils = require('../../lib/utils'), 6 | RID = require('orientjs').RID; 7 | 8 | var testSingleLevel = function(fnName){ 9 | var resultSet1 = { '@rid': new RID('#13:1'), property: 'value' }; 10 | var result = utils[fnName](resultSet1); 11 | assert.equal(result.id, '#13:1'); 12 | assert.equal(result.property, 'value'); 13 | assert(!result['@rid']); 14 | 15 | var resultSet2 = { '@rid': new RID('#13:1'), property: 'value', foreignKey: new RID('#13:2') }; 16 | var result = utils[fnName](resultSet2); 17 | assert.equal(result.id, '#13:1'); 18 | assert.equal(result.property, 'value'); 19 | assert.equal(result.foreignKey, '#13:2'); 20 | //assert.equal(typeof result.foreignKey, 'string'); // varies if its recursive or not 21 | assert(!result['@rid']); 22 | 23 | var resultSet3 = [{ '@rid': new RID('#13:1'), property: 'value' }, { '@rid': new RID('#13:2')}]; 24 | var result = utils[fnName](resultSet3); 25 | assert.equal(result.length, 2); 26 | assert.equal(result[0].id, '#13:1'); 27 | assert.equal(result[0].property, 'value'); 28 | assert.equal(result[1].id, '#13:2'); 29 | assert(!result[1]['@rid']); 30 | }; 31 | 32 | var testWithSchema= function(fnName){ 33 | var mockSchema = { 34 | modelKey: { 35 | model: 'some_model', 36 | columnName: 'modelColumnName' 37 | }, 38 | foreignKey: { 39 | type: 'string', 40 | foreignKey: true 41 | } 42 | }; 43 | 44 | var resultSet1 = { '@rid': new RID('#13:1'), property: 'value', foreignKey: new RID('#13:2') }; 45 | var result = utils[fnName](resultSet1, mockSchema); 46 | assert.equal(result.id, '#13:1'); 47 | assert.equal(result.property, 'value'); 48 | assert.equal(typeof result.foreignKey, 'string'); 49 | assert(result.foreignKey, '#13:2'); 50 | assert(!result['@rid']); 51 | 52 | var resultSet2 = { '@rid': new RID('#13:1'), property: 'value', modelColumnName: new RID('#13:2') }; 53 | var result = utils[fnName](resultSet2, mockSchema); 54 | assert.equal(typeof result.modelColumnName, 'string'); 55 | assert(result.modelColumnName, '#13:2'); 56 | }; 57 | 58 | describe('utils helper class', function() { 59 | 60 | describe('rewriteIds:', function() { 61 | var functionToTest = 'rewriteIds'; 62 | 63 | it('single level result set', function(done) { 64 | testSingleLevel(functionToTest); 65 | done(); 66 | }); 67 | 68 | 69 | it('with schema', function(done) { 70 | testWithSchema(functionToTest); 71 | done(); 72 | }); 73 | 74 | }); 75 | 76 | 77 | describe('rewriteIdsRecursive:', function() { 78 | var functionToTest = 'rewriteIdsRecursive'; 79 | 80 | it('invalid inputs', function(done) { 81 | var result = utils.rewriteIdsRecursive(null); 82 | assert.equal(result, null); 83 | result = utils.rewriteIdsRecursive([]); 84 | assert.equal(result.length, 0); 85 | result = utils.rewriteIdsRecursive([{}]); 86 | assert.equal(1, result.length); 87 | assert.equal(result[0].id, undefined); 88 | done(); 89 | }); 90 | 91 | it('single level result set', function(done) { 92 | testSingleLevel(functionToTest); 93 | done(); 94 | }); 95 | 96 | 97 | it('with schema', function(done) { 98 | testWithSchema(functionToTest); 99 | done(); 100 | }); 101 | 102 | 103 | it('multi level result set', function(done) { 104 | var resultSet1 = { '@rid': new RID('#13:1'), property: 'value', foreignKey: new RID('#13:2'), fetchedClass: { '@rid': new RID('#10:1')} }; 105 | var result = utils.rewriteIdsRecursive(resultSet1); 106 | assert.equal(result.id, '#13:1'); 107 | assert.equal(result.property, 'value'); 108 | assert.equal(typeof result.foreignKey, 'string'); 109 | assert(result.foreignKey, '#13:2'); 110 | assert.equal(result.fetchedClass.id, '#10:1'); 111 | assert(!result['@rid']); 112 | assert(!result.fetchedClass['@rid']); 113 | 114 | var resultSet2 = { '@rid': new RID('#13:1'), fetchedClass: { '@rid': new RID('#10:1'), nestedClass: { '@rid': new RID('#16:1') } } }; 115 | var result = utils.rewriteIdsRecursive(resultSet2); 116 | assert.equal(result.id, '#13:1'); 117 | assert.equal(result.fetchedClass.id, '#10:1'); 118 | assert.equal(result.fetchedClass.nestedClass.id, '#16:1'); 119 | assert(!result['@rid']); 120 | assert(!result.fetchedClass['@rid']); 121 | assert(!result.fetchedClass.nestedClass['@rid']); 122 | 123 | done(); 124 | }); 125 | 126 | 127 | it('circular reference', function(done) { 128 | 129 | var resultSet = { '@rid': new RID('#13:1'), child: { '@rid': new RID('#10:1') } }; 130 | resultSet.child.parent = resultSet; 131 | var result = utils.rewriteIdsRecursive(resultSet); 132 | assert.equal(result.id, '#13:1'); 133 | assert.equal(result.child.id, '#10:1'); 134 | assert.equal(result.child.parent.id, '#13:1'); 135 | assert(!result['@rid']); 136 | assert(!result.child['@rid']); 137 | 138 | done(); 139 | }); 140 | 141 | }); 142 | }); 143 | -------------------------------------------------------------------------------- /test/integration-orientdb/tests/performance/drop.test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | _ = require('lodash'); 3 | 4 | var self = this; 5 | 6 | // Require Fixtures 7 | var localFixturesPath = '../../fixtures/'; 8 | 9 | var fixtures = { 10 | TeamFixture: require('./fixtures/hasManyThrough.team.fixture.js'), 11 | StadiumFixture: require(localFixturesPath + 'hasManyThrough.stadium.fixture'), 12 | VenueFixture: require(localFixturesPath + 'hasManyThrough.venueHack.fixture'), 13 | FriendFixture: require(localFixturesPath + 'hasManyThrough.friend.fixture'), 14 | FollowsFixture: require(localFixturesPath + 'hasManyThrough.follows.fixture'), 15 | OwnsFixture: require(localFixturesPath + 'hasManyThrough.owns.fixture'), 16 | IndexesFixture: require(localFixturesPath + 'define.indexes.fixture'), 17 | PropertiesFixture: require(localFixturesPath + 'define.properties.fixture'), 18 | SchemalessPropertiesFixture: require(localFixturesPath + 'define.schemalessProperties.fixture'), 19 | DriverFixture: require(localFixturesPath + 'manyToMany.driverHack.fixture'), 20 | TaxiFixture: require('./fixtures/manyToMany.taxi.fixture'), 21 | UserFixture : { 22 | identity : 'user', 23 | attributes : { 24 | username : { 25 | type : 'string', 26 | unique : true 27 | }, 28 | passports : { 29 | collection : 'Passport', 30 | via : 'user', 31 | dominant : true 32 | } 33 | } 34 | }, 35 | PassportFixture : { 36 | identity : 'passport', 37 | attributes : { 38 | password : { 39 | type : 'string', 40 | minLength : 8 41 | }, 42 | user : { 43 | collection : 'User', 44 | via : 'passports' 45 | }, 46 | } 47 | } 48 | }; 49 | 50 | var baseConfig = { 51 | database : 'test_performance_drop', 52 | options : { 53 | unsafeDrop : false 54 | } 55 | } 56 | 57 | describe('Performance', function() { 58 | 59 | function elapsedTime (startPoint){ 60 | var diff = process.hrtime(startPoint); 61 | return (diff[0] * 1e9 + diff[1]) / 1e6; // divide by a million to get nano to milli 62 | } 63 | 64 | function initializeDB(context, config, cb) { 65 | CREATE_TEST_WATERLINE(context, config, _.cloneDeep(fixtures), function(err){ 66 | if(err) { return cb(err); } 67 | 68 | context.collections.User.create({ username: 'user1' }, function(err, user) { 69 | if(err) { return cb(err); } 70 | 71 | context.collections.Passport.create({ user: user.id, password: 'abcd5678' }, function(err, passport) { 72 | if(err) { return cb(err); } 73 | cb(); 74 | }); 75 | 76 | }); 77 | }); 78 | } 79 | 80 | describe('drop', function() { 81 | 82 | var safeDuration, unsafeDuration; 83 | 84 | describe('safely', function() { 85 | 86 | ///////////////////////////////////////////////////// 87 | // TEST SETUP 88 | //////////////////////////////////////////////////// 89 | 90 | before(function (done) { 91 | initializeDB(self, baseConfig, done); 92 | }); 93 | 94 | 95 | ///////////////////////////////////////////////////// 96 | // TEST METHODS 97 | //////////////////////////////////////////////////// 98 | 99 | it('should drop all fixtures in timely manner', function(done) { 100 | this.timeout(2000); 101 | var start = process.hrtime(); 102 | DELETE_TEST_WATERLINE(baseConfig, function(err){ 103 | if(err) { done(err); } 104 | safeDuration = elapsedTime(start); 105 | console.log('performance_drop:', safeDuration, 'ms'); 106 | done(); 107 | }); 108 | }); 109 | }); 110 | 111 | describe('unsafely', function() { 112 | 113 | ///////////////////////////////////////////////////// 114 | // TEST SETUP 115 | //////////////////////////////////////////////////// 116 | 117 | var unsafeConfig = { 118 | database : 'test_performance_drop_unsafe', 119 | options : { 120 | unsafeDrop : true 121 | } 122 | } 123 | 124 | var unsafeContex = {}; 125 | 126 | before(function (done) { 127 | initializeDB(unsafeContex, unsafeConfig, function(err){ 128 | if (err) { 129 | console.log(err); 130 | done(err); 131 | } 132 | done(); 133 | }); 134 | }); 135 | 136 | 137 | ///////////////////////////////////////////////////// 138 | // TEST METHODS 139 | //////////////////////////////////////////////////// 140 | 141 | it('should drop all fixtures in timely manner', function(done) { 142 | this.timeout(2000); 143 | var start = process.hrtime(); 144 | DELETE_TEST_WATERLINE(unsafeConfig, function(err){ 145 | if(err) { done(err); } 146 | unsafeDuration = elapsedTime(start); 147 | console.log('performance_drop_unsafe:', unsafeDuration, 'ms'); 148 | done(); 149 | }); 150 | }); 151 | 152 | it('unsafe should be faster than safe', function(done) { 153 | assert(unsafeDuration < safeDuration); 154 | done(); 155 | }); 156 | 157 | }); 158 | 159 | }); 160 | }); 161 | -------------------------------------------------------------------------------- /lib/record.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /** 3 | * Module Dependencies 4 | */ 5 | var _ = require('lodash'), 6 | RID = require('orientjs').RID, 7 | utils = require('./utils'), 8 | hop = utils.object.hop, 9 | log = require('debug-logger')('sails-orientdb:record'); 10 | 11 | /** 12 | * Record 13 | * 14 | * Represents a single record in a collection. Responsible for serializing values before 15 | * writing to a collection. 16 | * 17 | * @param {Object} values 18 | * @param {Object} schema 19 | * @api private 20 | */ 21 | 22 | var Record = module.exports = function Record(values, schema, connection, operation) { 23 | 24 | // Keep track of the current record's values 25 | this.values = {}; 26 | 27 | // Grab the schema for normalizing values 28 | this.schema = schema || {}; 29 | 30 | // Connection 31 | this.connection = connection; 32 | 33 | // operation type (create / insert) 34 | this.operation = operation; 35 | 36 | // If values were passed in, use the setter 37 | if(values){ 38 | var newValues = this.setValues(values); 39 | this.values = newValues.values; 40 | } 41 | 42 | return this; 43 | }; 44 | 45 | 46 | 47 | ///////////////////////////////////////////////////////////////////////////////// 48 | // PRIVATE METHODS 49 | ///////////////////////////////////////////////////////////////////////////////// 50 | 51 | 52 | /** 53 | * Set values 54 | * 55 | * Normalizes values into proper formats. 56 | * 57 | * @param {Object} values 58 | * @return {Object} 59 | * @api private 60 | */ 61 | 62 | Record.prototype.setValues = function setValues(values) { 63 | var results = this.serializeValues(values); 64 | this.normalizeId(results.values); 65 | 66 | return results; 67 | }; 68 | 69 | 70 | /** 71 | * Normalize ID's 72 | * 73 | * Moves values.id into the preferred orientDB @rid field. 74 | * 75 | * @param {Object} values 76 | * @api private 77 | */ 78 | Record.prototype.normalizeId = function normalizeId(values) { 79 | 80 | if(!values.hasOwnProperty('id') && !values.hasOwnProperty('@rid')) return; 81 | 82 | var id = values.id || values['@rid']; 83 | 84 | // Check if data.id looks like a RecordID 85 | if(_.isString(values.id) && utils.matchRecordId(values.id)) { 86 | id = new RID(values.id); 87 | } 88 | 89 | if(this.operation && this.operation === 'insert'){ 90 | delete values['@rid']; 91 | } else { 92 | values['@rid'] = id; 93 | } 94 | delete values.id; 95 | }; 96 | 97 | 98 | /** 99 | * Serialize Insert Values 100 | * 101 | * @param {Object} values 102 | * @return {Object} 103 | * @api private 104 | */ 105 | Record.prototype.serializeValues = function serializeValues(values) { 106 | var self = this; 107 | var returnResult = {}; 108 | 109 | Object.keys(values).forEach(function(key) { 110 | var schemaKey = key; 111 | if (!hop(self.schema, key)) { 112 | // only return if key is not a columnName 113 | var isColumnName = false; 114 | Object.keys(self.schema).forEach(function(attributeName) { 115 | if (self.schema[attributeName].columnName === key) { 116 | schemaKey = attributeName; 117 | isColumnName = true; 118 | } 119 | }); 120 | if (!isColumnName){ 121 | return; 122 | } 123 | } 124 | 125 | var type = self.schema[schemaKey].type; 126 | 127 | var foreignKey = self.schema[schemaKey].foreignKey || false; 128 | 129 | if (_.isUndefined(values[key]) || _.isNull(values[key])){ 130 | return; 131 | } 132 | 133 | var targetCollectionName = self.schema[schemaKey].model || self.schema[schemaKey].collection; 134 | 135 | // If a foreignKey, check if value matches a orientDB id and if so turn it into a recordId 136 | if (foreignKey && utils.matchRecordId(values[key])) { 137 | values[key] = new RID(values[key]); 138 | } 139 | else if (targetCollectionName && utils.matchRecordId(values[key])) { 140 | values[key] = new RID(values[key]); 141 | } 142 | else if (targetCollectionName && typeof values[key] === 'object') { 143 | log.warn('Unexpected behaviour: nested association for key [' + key + '], values: ', values[key]); 144 | } 145 | else if(targetCollectionName) { 146 | var targetCollection = self.connection.collectionsByIdentity[targetCollectionName]; 147 | if(targetCollection.primaryKey === 'id'){ 148 | log.warn('Nullifying foreign key [' + key + '] (value: [' + values[key] + 149 | ']) as value is not a RID and Waterline does not expect an error.'); 150 | values[key] = null; 151 | } 152 | } 153 | else if(foreignKey) { 154 | log.warn('Nullifying foreign key [' + key + '] (value: [' + values[key] + 155 | ']) as value is not a RID and Waterline does not expect an error.'); 156 | values[key] = null; 157 | } 158 | 159 | else if(type === 'json'){ 160 | if(!values[key]['@type']){ 161 | values[key]['@type'] = 'd'; 162 | } 163 | // workaround for #116: prevent sails-orientdb from sending null or empty @class 164 | // TODO: it probably can be made more elegant 165 | if(!values[key]['@class']){ 166 | delete values[key]['@class']; 167 | } 168 | } 169 | 170 | // TODO: should just be "type === 'binary'" but for some reason type never seems to 171 | // be equal to 'binary'. Waterline issue? 172 | else if ((type === 'binary' || !type) && Buffer.isBuffer(values[key])) { 173 | values[key] = values[key].toString('base64'); 174 | } 175 | }); 176 | 177 | returnResult.values = values; 178 | 179 | return returnResult; 180 | }; 181 | 182 | -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | # FAQ (Frequently Asked Questions) 2 | 3 | 4 | ### Which version should I use? 5 | 6 | The latest stable version in npm is always a safe bet. 7 | 8 | ```sh 9 | npm install sails-orientdb --save 10 | ``` 11 | 12 | [![NPM](https://nodei.co/npm/sails-orientdb.png?downloads=true&stars=true)](https://nodei.co/npm/sails-orientdb/) 13 | 14 | 15 | 16 | ### Where is the documentation? 17 | + Documentation for this module is in the [README.md](./README.md) file. 18 | + Docs for the latest stable npm release of Waterline are on [balderdashy/waterline-docs](https://github.com/balderdashy/waterline-docs). Sails documentation itself is on [sailsjs.org](http://sailsjs.org/#!documentation). 19 | 20 | 21 | ### What happened to waterline-orientdb? 22 | Srinath, Gaurav and Dário combined their efforts and now both are focused on developing a single Waterline/Sails OrientDB adapter, as such waterline-orientdb has been renamed **sails-orientdb**. More about this on the [README.md](./README.md#history). 23 | 24 | 25 | ### What is an adapter? 26 | 27 | Adapters expose **interfaces**, which imply a contract to implement certain functionality. This allows us to guarantee conventional usage patterns across multiple models, developers, apps, and even companies, making app code more maintainable, efficient, and reliable. Adapters are useful for integrating with databases, open APIs, internal/proprietary web services, or even hardware. 28 | 29 | 30 | 31 | ### How do I get involved? 32 | 33 | + [Contributing to this module](./CONTRIBUTING.md) 34 | + If you find a bug with this module, please submit an issue to the tracker in this repository. Better yet, send a pull request :) 35 | 36 | 37 | 38 | ## Why would I need a custom adapter? 39 | 40 | When building a Waterline / Sails app, the sending or receiving of any asynchronous communication with another piece of hardware can be normalized into an adapter. (viz. API integrations) 41 | 42 | > **From Wikipedia:** 43 | > *http://en.wikipedia.org/wiki/Create,_read,_update_and_delete* 44 | 45 | > Although a relational database provides a common persistence layer in software applications, numerous other persistence layers exist. CRUD functionality can be implemented with an object database, an XML database, flat text files, custom file formats, tape, or card, for example. 46 | 47 | In other words, Waterline is not just an ORM for your database. It is a purpose-agnostic, open standard and toolset for integrating with all kinds of RESTful services, datasources, and devices, whether it's LDAP, Neo4J, or [a lamp](https://www.youtube.com/watch?v=OmcQZD_LIAE). 48 | I know, I know... Not everything fits perfectly into a RESTful/CRUD mold! Sometimes the service you're integrating with has more of an RPC-style interface, with one-off method names. That's ok-- you can define any adapter methods you like! You still get all of the trickle-down config and connection-management goodness of Waterline core. 49 | 50 | 51 | 52 | ## What is an Adapter Interface? 53 | 54 | The functionality of adapters is as varied as the services they connect. That said, there is a standard library of methods, and a support matrix you should be aware of. Adapters may implement some, all, or none of the interfaces below, but rest assured that **if an adapter implements one method in an interface, it should implement *all* of them**. This is not always the case due to limitations and/or incomplete implementations, but at the very least, a descriptive error message should be used to keep developers informed of what's supported and what's not. 55 | 56 | > For more information, check out the Sails docs, and specifically the [adapter interface reference](https://github.com/balderdashy/sails-docs/blob/master/adapter-specification.md). 57 | 58 | 59 | 60 | ### Where do I get help? 61 | 62 | + [Ask a question on StackOverflow](http://stackoverflow.com/questions/tagged/sailsjs?sort=newest&days=30) 63 | + Get help from the [Google Group](https://groups.google.com/forum/#!forum/sailsjs) 64 | + Get help on IRC ([#sailsjs on freenode](http://irc.netsplit.de/channels/details.php?room=%23sailsjs&net=freenode)) 65 | + [Tweet @sailsjs](http://twitter.com/sailsjs) 66 | 67 | 68 | ### Why haven't I gotten a response to my feature request? 69 | 70 | When people see something working in practice, they're usually a lot more down to get on board with it! That's even more true in the open-source community, since most of us are not getting paid to do this (myself included). The best feature request is a pull request-- even if you can't do the whole thing yourself, if you blueprint your thoughts, it'll help everyone understand what's going on. 71 | 72 | ### I want to make a sweeping change / add a major feature 73 | It's always a good idea to contact the maintainer(s) of a module before doing a bunch of work. This is even more true when it affects how things work / breaks backwards compatibility. 74 | 75 | ### The maintainer of this module won't merge my pull request. 76 | 77 | Most of the time, when PRs don't get merged, a scarcity of time is to blame. I can almost guarantee you it's nothing personal :) And I can only speak for myself here, but in most cases, when someone follows up on a PR that's been sitting for a little while on Twitter, I don't mind the reminder at all. 78 | 79 | The best thing about maintaining lots of small modules is that it's trivial to override any one of them on their own. If you need some changes merged, please feel empowered to fork this model and release your own version. 80 | 81 | If you feel that yours is the better approach, and should be the default, share it with the community via IRC, Twitter, Google Groups, etc. Also, feel free to let the core Sails/Waterline team know and we'll take it into consideration. 82 | 83 | 84 | 85 | ### More questions? 86 | 87 | > If you have an unanswered question that isn't covered here, and that you feel would add value for the community, please feel free to send a PR adding it to this section. 88 | 89 | -------------------------------------------------------------------------------- /test/integration-orientdb/tests/associations/manyThrough.add.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | _ = require('lodash'); 3 | 4 | describe('Association Interface', function() { 5 | 6 | describe('n:m through association :: .add()', function() { 7 | 8 | ///////////////////////////////////////////////////// 9 | // TEST SETUP 10 | //////////////////////////////////////////////////// 11 | 12 | var stadiumRecord, teamRecord, stadiumMultiple, allTeams; 13 | 14 | before(function(done) { 15 | Associations.Stadium.create([{ name: 'hasManyThrough stadium'}, 16 | { name: 'hasManyThrough multiple stadium'}], function(err, stadiums) { 17 | if(err) return done(err); 18 | stadiumRecord = stadiums[0]; 19 | stadiumMultiple = stadiums[1]; 20 | Associations.Team.create([{ name: 'hasManyThrough team', mascot: 'elephant' }, 21 | { name: 'hasManyThrough team 1' }, 22 | { name: 'hasManyThrough team 2'}], 23 | function(err, teams) { 24 | if(err) return done(err); 25 | teamRecord = teams[0]; 26 | allTeams = teams; 27 | done(); 28 | }); 29 | }); 30 | }); 31 | 32 | describe('with an object', function() { 33 | 34 | ///////////////////////////////////////////////////// 35 | // TEST METHODS 36 | //////////////////////////////////////////////////// 37 | 38 | it('should link a stadium to a team through a join table', function(done) { 39 | stadiumRecord.teams.add(teamRecord.id); 40 | stadiumRecord.save(function(err){ 41 | assert(!err, err); 42 | 43 | Associations.Stadium.findOne(stadiumRecord.id) 44 | .populate('teams') 45 | .exec(function(err, stadium) { 46 | assert(!err, err); 47 | 48 | assert(Array.isArray(stadium.teams)); 49 | assert(stadium.teams.length === 1); 50 | assert(stadium.teams[0].mascot === 'elephant'); 51 | 52 | Associations.Stadium.findOne({'out().@rid': [teamRecord.id] }) 53 | .exec(function(err, stadiumWhichHas) { 54 | assert(!err, err); 55 | 56 | assert(stadiumWhichHas, "Edge has wrong direction"); 57 | assert(stadiumWhichHas.name === 'hasManyThrough stadium', "Edge has wrong direction"); 58 | done(); 59 | }); 60 | 61 | }); 62 | }); 63 | }); 64 | 65 | it('should link a stadium to several teams through a join table', function(done) { 66 | stadiumMultiple.teams.add(allTeams[1].id); 67 | stadiumMultiple.teams.add(allTeams[2].id); 68 | stadiumMultiple.save(function(err){ 69 | if(err) { done(err); } 70 | 71 | Associations.Stadium.findOne(stadiumMultiple.id) 72 | .populate('teams') 73 | .exec(function(err, stadium) { 74 | if(err) done(err); 75 | 76 | assert(Array.isArray(stadium.teams)); 77 | assert(stadium.teams.length === 2); 78 | assert(stadium.teams[0].name === 'hasManyThrough team 1'); 79 | assert(stadium.teams[1].name === 'hasManyThrough team 2'); 80 | done(); 81 | 82 | }); 83 | }); 84 | }); 85 | 86 | it('should link a team to a stadium through a join table', function(done) { 87 | //TODO: add EventEmitter2's mediator so tests can be ran async 88 | Associations.Team.findOne(teamRecord.id) 89 | .populate('stadiums') 90 | .exec(function(err, team) { 91 | assert(!err, err); 92 | 93 | assert(Array.isArray(team.stadiums)); 94 | assert(team.stadiums.length === 1); 95 | assert(team.stadiums[0].name === 'hasManyThrough stadium'); 96 | 97 | done(); 98 | }); 99 | }); 100 | 101 | }); 102 | 103 | describe('create nested associations()', function() { 104 | 105 | ///////////////////////////////////////////////////// 106 | // TEST METHODS 107 | //////////////////////////////////////////////////// 108 | 109 | it('should create a new stadium and team and associate them', function(done) { 110 | Associations.Stadium.create({ 111 | name: 'hasManyThrough aggregate stadium', 112 | teams: [{ name: 'hasManyThrough nested team' }] 113 | }, 114 | function(err, record) { 115 | assert(!err, err); 116 | 117 | Associations.Stadium.findOne(record.id) 118 | .populate('teams') 119 | .exec(function(err, stadium) { 120 | assert(!err, err); 121 | 122 | assert(Array.isArray(stadium.teams)); 123 | assert(stadium.teams.length === 1); 124 | assert(stadium.teams[0].name === 'hasManyThrough nested team'); 125 | done(); 126 | }); 127 | }); 128 | }); 129 | 130 | it('should create a new stadium, multiples teams and associate them', function(done) { 131 | Associations.Stadium.create({ 132 | name: 'hasManyThrough aggregate stadium multiples', 133 | teams: [ 134 | { name: 'hasManyThrough nested team 2' }, 135 | { name: 'hasManyThrough nested team 3' } 136 | ] 137 | }, 138 | function(err, record) { 139 | assert(!err, err); 140 | 141 | Associations.Stadium.findOne(record.id) 142 | .populate('teams') 143 | .exec(function(err, stadium) { 144 | assert(!err, err); 145 | 146 | assert(Array.isArray(stadium.teams)); 147 | assert(stadium.teams.length === 2); 148 | assert(stadium.teams[0].name === 'hasManyThrough nested team 2'); 149 | assert(stadium.teams[1].name === 'hasManyThrough nested team 3'); 150 | done(); 151 | }); 152 | }); 153 | }); 154 | 155 | }); 156 | 157 | }); 158 | }); 159 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | // JSHint Default Configuration File (as on JSHint website) 3 | // See http://jshint.com/docs/ for more details 4 | 5 | "maxerr" : 50, // {int} Maximum error before stopping 6 | 7 | // Enforcing 8 | "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.) 9 | "camelcase" : true, // true: Identifiers must be in camelCase 10 | "curly" : false, // true: Require {} for every new block or scope 11 | "eqeqeq" : false, // true: Require triple equals (===) for comparison 12 | "forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty() 13 | "freeze" : true, // true: prohibits overwriting prototypes of native objects such as Array, Date etc. 14 | "immed" : true, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` 15 | "indent" : 2, // {int} Number of spaces to use for indentation 16 | "latedef" : true, // true: Require variables/functions to be defined before being used 17 | "newcap" : true, // true: Require capitalization of all constructor functions e.g. `new F()` 18 | "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` 19 | "noempty" : true, // true: Prohibit use of empty blocks 20 | "nonbsp" : true, // true: Prohibit "non-breaking whitespace" characters. 21 | "nonew" : true, // true: Prohibit use of constructors for side-effects (without assignment) 22 | "plusplus" : false, // true: Prohibit use of `++` & `--` 23 | "quotmark" : false, // Quotation mark consistency: 24 | // false : do nothing (default) 25 | // true : ensure whatever is used is consistent 26 | // "single" : require single quotes 27 | // "double" : require double quotes 28 | "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) 29 | "unused" : true, // true: Require all defined variables be used 30 | "strict" : true, // true: Requires all functions run in ES5 Strict Mode 31 | "maxparams" : false, // {int} Max number of formal params allowed per function 32 | "maxdepth" : false, // {int} Max depth of nested blocks (within functions) 33 | "maxstatements" : false, // {int} Max number statements per function 34 | "maxcomplexity" : false, // {int} Max cyclomatic complexity per function 35 | "maxlen" : 120, // {int} Max number of characters per line 36 | 37 | // Relaxing 38 | "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) 39 | "boss" : false, // true: Tolerate assignments where comparisons would be expected 40 | "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. 41 | "eqnull" : false, // true: Tolerate use of `== null` 42 | "es5" : false, // true: Allow ES5 syntax (ex: getters and setters) 43 | "esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`) 44 | "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features) 45 | // (ex: `for each`, multiple try/catch, function expression…) 46 | "evil" : false, // true: Tolerate use of `eval` and `new Function()` 47 | "expr" : false, // true: Tolerate `ExpressionStatement` as Programs 48 | "funcscope" : false, // true: Tolerate defining variables inside control statements 49 | "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict') 50 | "iterator" : false, // true: Tolerate using the `__iterator__` property 51 | "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block 52 | "laxbreak" : false, // true: Tolerate possibly unsafe line breakings 53 | "laxcomma" : false, // true: Tolerate comma-first style coding 54 | "loopfunc" : false, // true: Tolerate functions being defined in loops 55 | "multistr" : false, // true: Tolerate multi-line strings 56 | "noyield" : false, // true: Tolerate generator functions with no yield statement in them. 57 | "notypeof" : false, // true: Tolerate invalid typeof operator values 58 | "proto" : false, // true: Tolerate using the `__proto__` property 59 | "scripturl" : false, // true: Tolerate script-targeted URLs 60 | "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` 61 | "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation 62 | "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` 63 | "validthis" : false, // true: Tolerate using this in a non-constructor function 64 | 65 | // Environments 66 | "browser" : false, // Web Browser (window, document, etc) 67 | "browserify" : false, // Browserify (node.js code in the browser) 68 | "couch" : false, // CouchDB 69 | "devel" : true, // Development/debugging (alert, confirm, etc) 70 | "dojo" : false, // Dojo Toolkit 71 | "jasmine" : false, // Jasmine 72 | "jquery" : false, // jQuery 73 | "mocha" : true, // Mocha 74 | "mootools" : false, // MooTools 75 | "node" : true, // Node.js 76 | "nonstandard" : false, // Widely adopted globals (escape, unescape, etc) 77 | "prototypejs" : false, // Prototype and Scriptaculous 78 | "qunit" : false, // QUnit 79 | "rhino" : false, // Rhino 80 | "shelljs" : false, // ShellJS 81 | "worker" : false, // Web Workers 82 | "wsh" : false, // Windows Scripting Host 83 | "yui" : false, // Yahoo User Interface 84 | 85 | // Custom Globals 86 | "globals" : { "Promise": true } // additional predefined global variables 87 | } -------------------------------------------------------------------------------- /test/integration-orientdb/bugs/43-orientdb_requestError/43-orientdb_requestError.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'), 2 | _ = require('lodash'), 3 | utils = require('../../../../lib/utils'); 4 | 5 | var self = this; 6 | 7 | describe('Bug #43: OrientDB.RequestError on update', function() { 8 | before(function (done) { 9 | var fixtures = { 10 | ImageFixture: require('./image.fixture'), 11 | SubprofileFixture: require('./post.fixture'), 12 | UserFixture: require('./user.fixture') 13 | }; 14 | CREATE_TEST_WATERLINE(self, 'test_bug_43', fixtures, done); 15 | }); 16 | after(function (done) { 17 | DELETE_TEST_WATERLINE('test_bug_43', done); 18 | }); 19 | 20 | describe('rodrigorn: update a created post', function() { 21 | ///////////////////////////////////////////////////// 22 | // TEST SETUP 23 | //////////////////////////////////////////////////// 24 | 25 | var postRecord, imageParent; 26 | 27 | before(function(done) { 28 | self.collections.Post.create({ title: 'a post' }, function(err, post) { 29 | if(err) { return done(err); } 30 | postRecord = post; 31 | 32 | self.collections.Image.create({ name: 'parent', crops: [ { name: 'crop' } ] }, function(err, img) { 33 | if(err) { return done(err); } 34 | imageParent = img; 35 | 36 | self.collections.Post.findOne(postRecord.id, function(err, thePost){ 37 | assert(!err); 38 | assert(thePost); 39 | 40 | thePost.image = img.id; 41 | 42 | self.collections.Post.update(postRecord.id, thePost, function(err, postUpdated){ 43 | if(err) { return done(err); } 44 | done(); 45 | }); 46 | }); 47 | }); 48 | }); 49 | }); 50 | 51 | 52 | ///////////////////////////////////////////////////// 53 | // TEST METHODS 54 | //////////////////////////////////////////////////// 55 | 56 | it('should update a post', function(done) { 57 | self.collections.Post.findOne(postRecord.id) 58 | .then(function(post){ 59 | assert(post); 60 | 61 | post.title = 'new title'; 62 | 63 | self.collections.Post.update(post.id, post, function(err, post2){ 64 | assert(!err, err); 65 | assert.equal(post.title, 'new title'); 66 | done(); 67 | }); 68 | }) 69 | .error(done); 70 | }); 71 | 72 | it('control test: should have a crop associated', function(done) { 73 | self.collections.Image.findOne(imageParent.id) 74 | .populate('crops') 75 | .exec(function(err, imgParent) { 76 | if(err) { return done(err); } 77 | assert.equal(imgParent.crops[0].name, 'crop'); 78 | done(); 79 | }); 80 | }); 81 | 82 | it('control test: should have a crop associated', function(done) { 83 | self.collections.Image.findOne(imageParent.id) 84 | .exec(function(err, imgParent) { 85 | if(err) { return done(err); } 86 | 87 | imgParent.isCrop = false; 88 | self.collections.Image.update(imageParent.id, imgParent, function(err, res){ 89 | if(err) { return done(err); } 90 | assert.equal(imgParent.isCrop, false); 91 | done(); 92 | }); 93 | }); 94 | }); 95 | 96 | it('control test: should have a crop associated', function(done) { 97 | self.collections.Image.findOne(imageParent.id) 98 | .exec(function(err, imgParent) { 99 | if(err) { return done(err); } 100 | 101 | imgParent.isCrop = false; 102 | self.collections.Image.update(imageParent.id, imgParent, function(err, res){ 103 | if(err) { return done(err); } 104 | assert.equal(imgParent.isCrop, false); 105 | done(); 106 | }); 107 | }); 108 | }); 109 | 110 | }); 111 | 112 | 113 | describe('stackoverflow issue: update a created user', function() { 114 | ///////////////////////////////////////////////////// 115 | // TEST SETUP 116 | //////////////////////////////////////////////////// 117 | 118 | var userParent, userChild; 119 | 120 | before(function(done) { 121 | self.collections.Dbuser.create({ username: 'parent' }, function(err, user) { 122 | if(err) { return done(err); } 123 | userParent = user; 124 | 125 | self.collections.Dbuser.create({ username: 'child' }, function(err, user2) { 126 | if(err) { return done(err); } 127 | userChild = user2; 128 | 129 | self.collections.Dbuser.findOne(userParent.id, function(err, dbUser){ 130 | if(err) { return done(err); } 131 | dbUser.follows.add(user2.id); 132 | dbUser.save(done); 133 | }) 134 | 135 | }); 136 | }); 137 | }); 138 | 139 | 140 | ///////////////////////////////////////////////////// 141 | // TEST METHODS 142 | //////////////////////////////////////////////////// 143 | 144 | it('control test: should have created child user', function(done) { 145 | self.collections.Dbuser.findOne({ username: 'child' }) 146 | .populate('followed') 147 | .exec(function(err, user) { 148 | assert(!err, err); 149 | assert.equal(user.username, 'child'); 150 | assert.equal(user.followed[0].username, 'parent'); 151 | done(); 152 | }); 153 | }); 154 | 155 | it('should update user', function(done) { 156 | userParent.token = 'iasbdasgdpsabçefbe'; 157 | self.collections.Dbuser.update(userParent.id, userParent, function(err, user) { 158 | if(err) { return done(err); } 159 | assert(user); 160 | done(); 161 | }); 162 | }); 163 | 164 | xit('should create 2 users who reference each other', function(done) { 165 | self.collections.Dbuser.update({ username: 'user1', follows: [ { username: 'user2' } ] }, function(err, user) { 166 | if(err) { return done(err); } 167 | assert(user); 168 | done(); 169 | }); 170 | }); 171 | 172 | }); 173 | 174 | }); 175 | -------------------------------------------------------------------------------- /lib/query.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /** 3 | * Module dependencies 4 | */ 5 | var _ = require('lodash'), 6 | utils = require('./utils'), 7 | hop = utils.object.hop, 8 | RID = require('orientjs').RID; 9 | 10 | 11 | /** 12 | * Query Constructor 13 | * 14 | * Normalizes Waterline queries to work with Oriento. 15 | * 16 | * @param {Object} options 17 | * @api private 18 | */ 19 | var Query = module.exports = function Query(options, connection) { 20 | 21 | // Sequel builder 22 | this.sequel = connection.sequel; 23 | 24 | // decode 25 | this.decodeURIComponent = connection.config.options.decodeURIComponent; 26 | 27 | // Normalize Criteria 28 | this.criteria = this.normalizeCriteria(options); 29 | 30 | return this; 31 | }; 32 | 33 | 34 | /** 35 | * Normalize Criteria 36 | * 37 | * Transforms a Waterline Query into a query that can be used 38 | * with Oriento. For example it sets '>' to $gt, etc. 39 | * 40 | * @param {Object} options 41 | * @return {Object} 42 | * @api private 43 | */ 44 | Query.prototype.normalizeCriteria = function normalizeCriteria(options) { 45 | var self = this; 46 | 47 | return _.mapValues(options, function (original, key) { 48 | if (key === 'where') return self.parseWhere(original); 49 | return original; 50 | }); 51 | }; 52 | 53 | 54 | /** 55 | * Parse Where 56 | * 57 | * ::= 58 | * 59 | * @api private 60 | * 61 | * @param original 62 | * @returns {*} 63 | */ 64 | Query.prototype.parseWhere = function parseWhere(original) { 65 | var self = this; 66 | 67 | // Fix an issue with broken queries when where is null 68 | //if(_.isNull(original)) return {}; 69 | if(_.isNull(original)) return null; 70 | 71 | return self.parseClause(original); 72 | }; 73 | 74 | 75 | /** 76 | * Parse Clause 77 | * 78 | * ::= { , ... } 79 | * 80 | * ::= : 81 | * | or|$or: [, ...] 82 | * | $or : [, ...] 83 | * | $and : [, ...] 84 | * | $nor : [, ...] 85 | * | like : { : , ... } 86 | * 87 | * @api private 88 | * 89 | * @param original 90 | * @returns {*} 91 | */ 92 | Query.prototype.parseClause = function parseClause(original) { 93 | var self = this; 94 | 95 | return self.fixId(original); 96 | }; 97 | 98 | 99 | 100 | /** 101 | * Convert IDs into RIDs 102 | * 103 | * ::= { , ... } 104 | * 105 | * ::= : 106 | * | or|$or: [, ...] 107 | * | $or : [, ...] 108 | * | $and : [, ...] 109 | * | $nor : [, ...] 110 | * | like : { : , ... } 111 | * 112 | * @api private 113 | * 114 | * @param original 115 | * @returns {*} 116 | */ 117 | Query.prototype.fixId = function fixId(original) { 118 | var self = this; 119 | 120 | return _.reduce(original, function parseClausePair(obj, val, key) { 121 | 122 | // handle Logical Operators 123 | if (['or', 'and', 'nor'].indexOf(key) !== -1) { 124 | // Value of or, and, nor require an array, else ignore 125 | if (_.isArray(val)) { 126 | val = _.map(val, function (clause) { 127 | return self.parseClause(clause); 128 | }); 129 | obj[key] = val; 130 | } 131 | } 132 | 133 | // Default 134 | else { 135 | // Normalize `id` key into orientdb `@rid` 136 | if (key === 'id' && !hop(this, '@rid')) { 137 | key = '@rid'; 138 | obj[key] = self.decode(val); 139 | } else if(key === '@rid') { 140 | obj[key] = self.decode(val); 141 | } else { 142 | obj[key] = val; 143 | } 144 | } 145 | 146 | return obj; 147 | }, {}, original); 148 | }; 149 | 150 | 151 | /** 152 | * Decodes ID from encoded URI component 153 | * 154 | * @api private 155 | * 156 | * @param {Array|Object|String} idValue 157 | * @returns {Array|String} 158 | */ 159 | Query.prototype.decode = function decode(idValue) { 160 | var self = this; 161 | 162 | if(! idValue || !self.decodeURIComponent) { return idValue; } 163 | 164 | function decodeURI(id){ 165 | var res = id; 166 | if(id.indexOf('%23') === 0){ 167 | res = res.replace('%23', '#'); 168 | res = res.replace('%3A', ':'); 169 | } 170 | return res; 171 | } 172 | 173 | if(_.isString(idValue)){ 174 | return decodeURI(idValue); 175 | } 176 | 177 | if(_.isArray(idValue)){ 178 | return _.map(idValue, decodeURI); 179 | } 180 | 181 | if(_.isPlainObject(idValue)){ 182 | return _.reduce(idValue, function(result, val, key) { 183 | result[key] = _.isString(val) ? decodeURI(val) : 184 | _.isArray(val) ? _.map(val, decodeURI) : 185 | val; 186 | return result; 187 | }, {}); 188 | } 189 | 190 | return idValue; 191 | }; 192 | 193 | 194 | /** 195 | * Get Select Query 196 | * 197 | * @api public 198 | * @param {String} collection 199 | * @returns {Object} 200 | */ 201 | Query.prototype.getSelectQuery = function getSelectQuery(collection, attributes) { 202 | var self = this; 203 | 204 | var _query = self.sequel.find(collection, self.criteria); 205 | _query.query[0] = _query.query[0].replace(collection.toUpperCase(), collection); 206 | _query.params = _.reduce(_query.values[0], function(accumulator, value, index){ 207 | var key = _query.keys[0][index]; 208 | var attribute = utils.getAttributeAsObject(attributes, key) || {}; 209 | var foreignKeyOrId = key === '@rid' || key && key.indexOf('.@rid') !== -1 || 210 | attribute.foreignKey || attribute.model || false; 211 | var paramValue = foreignKeyOrId && _.isString(value) && utils.matchRecordId(value) ? new RID(value) : value; 212 | 213 | accumulator['param' + index] = paramValue; 214 | return accumulator; 215 | }, {}); 216 | 217 | return _query; 218 | }; 219 | 220 | 221 | /** 222 | * Get Where Query 223 | * 224 | * @api public 225 | * @param {String} collection 226 | * @returns {Object} 227 | */ 228 | Query.prototype.getWhereQuery = function getWhereQuery(collection) { 229 | var self = this; 230 | 231 | var _where = self.getSelectQuery(collection); 232 | _where.query[0] = _where.query[0].split('WHERE')[1]; 233 | _where.query[0] = _where.query[0] && _where.query[0].trim() !== '' ? _where.query[0] : null; 234 | 235 | return _where; 236 | }; 237 | 238 | 239 | 240 | -------------------------------------------------------------------------------- /test/unit/associations.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Test dependencies 3 | */ 4 | var assert = require('assert'), 5 | util = require('util'), 6 | Collection = require('../../lib/collection'), 7 | Associations = require('../../lib/associations'), 8 | _ = require('lodash'); 9 | 10 | var collections = { 11 | comment: require('./fixtures/comment.model'), 12 | profile: require('./fixtures/profile.model'), 13 | recipe_content: require('./fixtures/recipeContent.model'), 14 | authored_comment: require('./fixtures/authoredComment.model'), 15 | comment_parent: require('./fixtures/commentParent.model'), 16 | comment_recipe: require('./fixtures/commentRecipe.model') 17 | }; 18 | 19 | var waterlineSchema = _.cloneDeep(collections); 20 | Object.keys(waterlineSchema).forEach(function(key){ 21 | var collection = waterlineSchema[key]; 22 | Object.keys(collection.attributes).forEach(function(id){ 23 | var attribute = collection.attributes[id]; 24 | if(attribute.through){ 25 | attribute.collection = attribute.through; 26 | } 27 | }); 28 | }); 29 | 30 | var connectionMock = { 31 | config: { options: {fetchPlanLevel: 1} }, 32 | waterlineSchema: waterlineSchema 33 | }; 34 | 35 | var newCollections = {}; 36 | Object.keys(collections).forEach(function(key){ 37 | collections[key].definition = collections[key].attributes; 38 | newCollections[key] = new Collection(collections[key], connectionMock, collections); 39 | }); 40 | newCollections.authored_comment = new Collection.Edge(collections.authored_comment, connectionMock, collections); 41 | newCollections.comment_parent = new Collection.Edge(collections.comment_parent, connectionMock, collections); 42 | newCollections.comment_recipe = new Collection.Edge(collections.comment_recipe, connectionMock, collections); 43 | connectionMock.collections = newCollections; 44 | connectionMock.collectionsByIdentity = newCollections; 45 | 46 | 47 | var associations = new Associations(connectionMock); 48 | 49 | describe('associations class', function () { 50 | 51 | it('getVerticesFromEdges: should extract vertices from edges in several different scenarios', function (done) { 52 | 53 | var singleEdgeId = associations.getVerticesFromEdges('#14:0', 'out'); 54 | assert.equal(singleEdgeId.length, 0, 'singe edge id'); 55 | 56 | var edgeRefs = associations.getVerticesFromEdges(['14:0', '#14:1'], 'out'); 57 | assert.equal(edgeRefs.length, 0); 58 | 59 | var singleEdgeVertexId = associations.getVerticesFromEdges({ out: '#13:2' }, 'out'); 60 | assert.equal(singleEdgeVertexId.length, 1); 61 | assert.equal(singleEdgeVertexId[0].id, '#13:2'); 62 | 63 | var singleEdgeVertexObject = associations.getVerticesFromEdges({ out: { '@rid': '#13:2' } }, 'out'); 64 | assert.equal(singleEdgeVertexObject.length, 1); 65 | assert.equal(singleEdgeVertexObject[0]['@rid'], '#13:2'); 66 | 67 | var edgeArrayVertexId = associations.getVerticesFromEdges([{ out: '#13:2' }], 'out'); 68 | assert.equal(edgeArrayVertexId.length, 1); 69 | assert.equal(edgeArrayVertexId[0].id, '#13:2', 'actual edgeArrayVertexId: ' + util.inspect(edgeArrayVertexId)); 70 | 71 | var edgeArrayVertexObject = associations.getVerticesFromEdges([{ out: { '@rid': '#13:2' } }], 'out'); 72 | assert.equal(edgeArrayVertexObject.length, 1); 73 | assert.equal(edgeArrayVertexObject[0]['@rid'], '#13:2'); 74 | 75 | var edgeArrayMixedObjects = associations.getVerticesFromEdges([{ out: { '@rid': '#13:2' } }, { out: '#13:1' }, '14:0', '#14:1'], 'out'); 76 | assert.equal(edgeArrayMixedObjects.length, 2); 77 | assert.equal(edgeArrayMixedObjects[0]['@rid'], '#13:2'); 78 | assert.equal(edgeArrayMixedObjects[1].id, '#13:1'); 79 | 80 | done(); 81 | }); 82 | 83 | 84 | it('getFetchPlan: check fetch plan query is built correctly', function(done){ 85 | var joins = [ 86 | { 87 | "parent": "comment", 88 | "parentKey": "id", 89 | "child": "authored_comment", 90 | "childKey": "commentRef", 91 | "select": false, 92 | "alias": "author", 93 | "removeParentKey": false, 94 | "model": false, 95 | "collection": true 96 | }, 97 | { 98 | "parent": "authored_comment", 99 | "parentKey": "profileRef", 100 | "child": "profile", 101 | "childKey": "id", 102 | "select": false, 103 | "alias": "author", 104 | "junctionTable": true, 105 | "removeParentKey": false, 106 | "model": false, 107 | "collection": true, 108 | "criteria": { "where": {}} 109 | }, 110 | { 111 | "parent": "comment", 112 | "parentKey": "id", 113 | "child": "comment_parent", 114 | "childKey": "childRef", 115 | "select": false, 116 | "alias": "parent", 117 | "removeParentKey": false, 118 | "model": false, 119 | "collection": true 120 | }, 121 | { 122 | "parent": "comment_parent", 123 | "child": "comment", 124 | "childKey": "id", 125 | "select": false, 126 | "alias": "parent", 127 | "junctionTable": true, 128 | "removeParentKey": false, 129 | "model": false, 130 | "collection": true, 131 | "criteria": { "where": {}} } 132 | ]; 133 | 134 | var fetchPlan = associations.getFetchPlan('comment', { joins: joins }); 135 | assert.equal(fetchPlan.where, 'in_authored_comment:1 in_authored_comment.out:1 out_comment_parent:1 out_comment_parent.in:1 out_comment_recipe:1'); 136 | 137 | var fetchPlan2 = associations.getFetchPlan('comment', { joins: joins }, 2); 138 | assert.equal(fetchPlan2.where, 'in_authored_comment:1 in_authored_comment.out:2 out_comment_parent:1 out_comment_parent.in:2 out_comment_recipe:1'); 139 | 140 | done(); 141 | }); 142 | 143 | 144 | it('expandResults: should replace foreign keys with cloned records', function(done){ 145 | var resultset = [ 146 | { id: '#5:0', name: 'Maria', out_edge: { out: '#5:0', in: '#4:0' } } 147 | ]; 148 | 149 | var expandedResultset = [ 150 | { id: '#5:0', name: 'Maria', out_edge: { out: { id: '#5:0', name: 'Maria', out_edge: { out: '#5:0', in: '#4:0' } }, 151 | in: '#4:0' } } 152 | ]; 153 | 154 | var transformed = associations.expandResults(resultset); 155 | assert(_.isEqual(transformed, expandedResultset), 'instead got: ' + util.inspect(transformed)); 156 | 157 | done(); 158 | }); 159 | 160 | it('expandResults: should not confuse ids with rids', function(done){ 161 | var resultset = [ 162 | { id: '#5:0', name: 'Maria', picture: { id: 1 }, likes: 1 } 163 | ]; 164 | 165 | var expandedResultset = [ 166 | { id: '#5:0', name: 'Maria', picture: { id: 1 }, likes: 1 } 167 | ]; 168 | 169 | var transformed = associations.expandResults(resultset); 170 | assert.deepEqual(transformed, expandedResultset); 171 | 172 | done(); 173 | }); 174 | 175 | }); 176 | --------------------------------------------------------------------------------