GithubHelp home page GithubHelp logo

node-simple-odata-server's Introduction

⚠️ This repository isn't being maintained. It's stable and still used in jsreport, but we are too busy to provide adequate maintenance. Don't hesitate to let me know if you plan to maintain a fork so I can share it here..

Node simple OData server

NPM Version License build status

Super simple implementation of OData server running on Node.js with easy adapters for mongodb and nedb. Just define an OData model, provide a mongo or nedb database, hook into node.js http server and run.

It supports basic operations you would expect like providing $metadata, filtering and also operations for insert, update and delete. On the other hand it suppose to be really simple so you don't get support for entity links, batch operations, atom feeds and many others.

The implementation is tested with .net OData client and it should fulfill basic protocol requirements.

Get started

This is how you can create an OData server with node.js http module and nedb.

var http = require('http');
var Datastore = require('nedb');
var db = new Datastore( { inMemoryOnly: true });
var ODataServer = require('simple-odata-server');
var Adapter = require('simple-odata-server-nedb');

var model = {
    namespace: "jsreport",
    entityTypes: {
        "UserType": {
            "_id": {"type": "Edm.String", key: true},
            "test": {"type": "Edm.String"},            
        }
    },   
    entitySets: {
        "users": {
            entityType: "jsreport.UserType"
        }
    }
};

var odataServer = ODataServer("http://localhost:1337")
    .model(model)
    .adapter(Adapter(function(es, cb) { cb(null, db)}));


http.createServer(odataServer.handle.bind(odataServer)).listen(1337);

Now you can try requests like:
GET http://localhost:1337/$metadata
GET http://localhost:1337/users?$filter=test eq 'a' or test eq 'b'&$skip=1&$take=5
GET http://localhost:1337/users('aaaa')
GET http://localhost:1337/users?$orderby=test desc
GET http://localhost:1337/users/$count
POST, PATCH, DELETE

Adapters

There are currently two adapters implemented.

The mongo adapter can be used as

var Adapter = require('simple-odata-server-mongodb')
MongoClient.connect(url, function(err, db) {
	odataServer.adapter(Adapter(function(cb) { 
		cb(err, db.db('myodatadb')); 
	})); 
});

express.js

It works well also with the express.js. You even don't need to provide service uri in the ODataServer constructor because it is taken from the express.js request.

app.use("/odata", function (req, res) {
        odataServer.handle(req, res);
});

cors

You can quickly set up cors without using express and middlewares using this call

odataServer.cors('*')

Configurations

Using existing adapter is just a simple way for initializing ODataServer. You can implement your own data layer or override default behavior using following methods:

odataServer
	.query(fn(setName, query, req, cb))
	.update(fn(setName, query, update, req, cb))
	.insert(fn(setName, doc, req, cb))
	.remove(fn(setName, query, req, cb))
	.beforeQuery(fn(setName, query, req, cb))
	.beforeUpdate(fn(setName, query, req, update))
	.beforeInsert(fn(setName, doc, req, cb))
	.beforeRemove(fn(setName, query, req, cb))
	.afterRead(fn(setName, result));
	//add hook to error which you can handle or pass to default
	.error(fn(req, res, error, default))

Contributions

I will maintain this repository for a while because I use it in jsreport. You are more than welcome to contribute with pull requests and add other basic operations you require.

Limitations

  • document ids must have name _id
  • no entity links
  • no batch operations
  • no validations
  • ... this would be a very long list, so rather check yourself

License

See license

node-simple-odata-server's People

Contributors

bjrmatos avatar chandruxp avatar glapcio avatar hakandilek avatar leboff avatar pmarcely avatar pofider avatar ranawaysuccessfully avatar sushovan861 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

node-simple-odata-server's Issues

insert.js -- missing 2 /'s

lines 42 and 43 are missing a / between cfg.serviceUrl and req.params.collection

current lines 42 and 43:
entity["@odata.id"] = cfg.serviceUrl + req.params.collection + "/('" + entity._id + "')"; entity["@odata.editLink"] = cfg.serviceUrl + req.params.collection + "/('" + entity._id + "')";

suggested (works for me anyway)
entity["@odata.id"] = cfg.serviceUrl + "/" + req.params.collection + "/('" + entity._id + "')"; entity["@odata.editLink"] = cfg.serviceUrl + "/" + req.params.collection + "/('" + entity._id + "')";

otherwise, this is nifty server. thanks!

TypeError: simpleOdataServer.adapter is not a function

Tested in my own system, as well as on npm.runkit, always getting same error:
TypeError: simpleOdataServer.adapter is not a function

Demo-Code from runkit:

var mongoClt = require('mongodb').MongoClient;
var simpleOdataServer = require("simple-odata-server");
var Adapter = require('simple-odata-server-mongodb');
var url = "127.0.0.1:27017";

mongoClt.connect(url, function(err, db) {
    simpleOdataServer.adapter(Adapter(function(cb) { cb(err, db); })); 
});

Unable to filter on nested property in a collection

I tried a couple of ways to query on nested property:
$expand=addresses($filter=city eq 'Seattle')
$expand=addresses($filter=addresses/city eq 'Seattle')
$filter=addresses/city eq 'Seattle'

However it errors out with http 500 error.

Are all responses from the server wrapped in value property

Hi,

Are all responses from the server wrapped in value property?
For example, if i use a custom implementation of the data layer by overriding query and return - { "_id": "yes", "business_key": "yes yes", "email": "[email protected]"} - to the callback function. Then the odata API returns that object and the same object wrapped inside a value object.

{
"@odata.context": "http://localhost:1337/$metadata#users/$entity",
"_id": "yes",
"business_key": "yes yes",
"email": "[email protected]",
"value": {
"_id": "yes",
"business_key": "yes yes",
"email": "[email protected]"
}
}

Defect(?): Need sample for use with Mongodb

I am bit puzzled. I cannot get a sample to work

I can get the plain sample to work. The next one, I cannot. Can you check the Gist?

Mongodb

  • Host: localhost:27017
  • Database: jsreport
  • Collection: jsreport
  • Authentication: none

Here's what I got. I must have set something wrong
Gist

Geospatial support?

Hello,

Found this in OData archives.
Question: what is the support provided by node-simple-odata-server?

Thanks!

OData v2.0

Does this library support OData v2.0? I'm creating an automated test suite for code that talks to an OData v2.0 service and it would be great to be able to use this off the shelf!

Cheers,

Ben

Hook samples

Hello,

Could you please provide some hook samples?
Let's say I have an Express endpoint and before inserting in an specific set, I wish to do something and then proceed with the default behavior.

What I tried:

var model = {
	namespace: "testes",
	entityTypes: {
		"Document": {
			"_id": {
				"type": "Edm.String",
				key: true
			},
			"tipo": {
				"type": "Edm.Int32"
			}
		}
	},
	entitySets: {
		"documents": {
			entityType: "testes.Document"
		}
	}
};

odataServer.beforeInsert((setName, doc, req, cb) => {
	if (setName == 'documents') {
		console.log('test!');
	}
});

app.use("/odata", function (req, res) {
	odataServer.handle(req, res);
});
...

I see test logged in console but there is no response anymore.
What should I do next?

Thanks

typo in "Get Started" section

It seems to me the name of the package as listed in the "Get Started" section does not match what was published in npm:

Wrong: var ODataServer = require("odata-simple-server");
Right: var ODataServer = require("simple-odata-server");

--Bob

Add my own database?

I have a postgresql database with JSONB documents. Is it possible to use this inside node-simple-odata-server via a translation layer of some kind?

Extending the query with $batch & $search

Hi @pofider.
node-simple-odata-server is using auth0's node-odata-parser, which doesn't parse the $batch & $search query parameters. Is there any way to extend the node-simple-odata-server to support $batch & $search and anything else that's not being parsed by the parser?

node-odata-parser is achieved, so it's probably not possible to extend the parser to include the $batch and other features.

Need more information about Configuration

AIM: I want to add filters and manipulate collections names before data fetch from DB.

npm in use:

  1. 'simple-odata-server'
  2. 'simple-odata-server-mongodb'

Hello,
Can you provide some example as to how one might use Configuration. particularly query.

For example:
If user request something like http://localhost:1337/data, It means data collection will be fetch and converted to odata format.
I want to add some filter when data collection is fetched. I also want to override the name of the data collection internally (@odata.context refers to data not the overridden collection name ).

Request header field Access-Control-Allow-Origin is not allowed by Access-Control-Allow-Headers in preflight response.

Hi guys!

Please correct me if i am wrong, but had this issue, that i get back this error:
Request header field Access-Control-Allow-Origin is not allowed by Access-Control-Allow-Headers in preflight response.

I set the odataServer.cors('*') , and as i can see in my preflight call in the response header i get it back nicely: access-control-allow-origin : *
but shouldn't this also bee sent with not just the options call?

https://mdn.mozillademos.org/files/14289/prelight.png -> this is from https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Preflighted_requests

Non request depending

Hello,

Considering an ODataServer configured with MongoDB and a model, is it possible to fire an "insert" from node application, without a POST request?

Thanks

Sub Level Complex Type values are stored, but unable to retrieve

Have used the library to create odata service. Create an Entity 'UserType' with a Complex Type 'Address' which has a sub complex type 'Location'.
The data gets saved in the db.
But when i run the command to fetch, the Location always comesempty.

{
            "_id": "640f052702b849252f020415",
            "test": "joe",
            "num": "55",
            "addresses": {
                "street": "101-Main",
                "location": [
                    {}
                ]
            }
        }

The data in DB :

{
  "_id": "640f052702b849252f020415",
  "test": "joe",
  "num": "55",
  "addresses": {
    "street": "101-Main",
    "location": [
      {
        "country": "UK",
        "continent": "Europe"
      }
    ]
  }
}

Query i used:
{my url}/UserType?$expand=AddressType/Location

/$count support

http://127.0.0.1:3010/odata/users/$count
returns
{"@odata.context":"http://127.0.0.1:3010/odata/$metadata#users","value":14178}
but Microsoft.OData.Client expects raw value 14178.

need to customize insert query

Dear,
I would like to configure insert (POST) future, where additionally I would like to add code for generating token.
Can you help me, with any sample code. Where other features I need ASIS.
Regards,
Devender

Example throws exception

Hi,
when I try to run your example from the examples folder I get the following error as soon as I try to read the users entityset.

/Users/helmut/projekte/node/simple-odata-server-test/node_modules/simple-odata-server/lib/prune.js:15
        var propDef = type[prop];
                          ^
TypeError: Cannot read property '_id' of undefined
    at prune (/Users/helmut/projekte/node/simple-odata-server-test/node_modules/simple-odata-server/lib/prune.js:15:27)
    at prune (/Users/helmut/projekte/node/simple-odata-server-test/node_modules/simple-odata-server/lib/prune.js:5:13)
    at module.exports (/Users/helmut/projekte/node/simple-odata-server-test/node_modules/simple-odata-server/lib/prune.js:54:9)
    at ODataServer.pruneResults (/Users/helmut/projekte/node/simple-odata-server-test/node_modules/simple-odata-server/lib/odataServer.js:226:5)
    at /Users/helmut/projekte/node/simple-odata-server-test/node_modules/simple-odata-server/lib/query.js:74:13
    at /Users/helmut/projekte/node/simple-odata-server-test/node_modules/simple-odata-server/lib/odataServer.js:139:13
    at /Users/helmut/projekte/node/simple-odata-server-test/node_modules/simple-odata-server/lib/nedbAdapter.js:56:24
    at queue.async.queue.callback (/Users/helmut/projekte/node/simple-odata-server-test/node_modules/nedb/lib/executor.js:30:17)
    at Cursor.execFn (/Users/helmut/projekte/node/simple-odata-server-test/node_modules/nedb/lib/datastore.js:424:12)
    at Cursor._exec (/Users/helmut/projekte/node/simple-odata-server-test/node_modules/nedb/lib/cursor.js:172:17)

Unfortunately I'm not able to debug with node-debug (cannot set a breakpoint, don't know why, I'm quite new to node).
Any ideas whats wrong with the example / the odata-server?

Here's my code:

var http = require('http');
var Datastore = require('nedb');
var db = new Datastore( { inMemoryOnly: true });
var ODataServer = require("simple-odata-server");

var model = {
    namespace: "myns",
    entityTypes: {
        "myns.UserType": {
            "_id": {"type": "Edm.String", key: true},
            "test": {"type": "Edm.String"},            
        }
    },   
    entitySets: {
        "users": {
            entityType: "myns.UserType"
        }
    }
};

var odataServer = ODataServer("http://localhost:1337")
    .model(model)
    .onNeDB(function(es, cb) { cb(null, db)});


http.createServer(odataServer.handle.bind(odataServer)).listen(1337);

db.insert({"_id": "1", "test": "a"});
db.insert({"_id": "2", "test": "b"});
db.insert({"_id": "3", "test": "c"});
db.insert({"_id": "4", "test": "d"});
db.insert({"_id": "5", "test": "e"});

Best regards
Helmut

Example does not run

Hi,

I'm trying to get the "Get Started" example to work. After correcting the package name, the code runs but accessing "http://localhost:1337/$metadata" results in:

{
  "error": {
    "code": 500,
    "message": "Missing attribute value for attribute EntityType of element EntitySet",
    "stack": "Error: Missing attribute value for attribute EntityType of element EntitySet\n    
at new XMLAttribute (\/Users\/bobmayo56\/Documents\/MayoPrivate\/proj\/kkbobquery\/web\/node_modules\/simple-odata-server\/node_modules\/xmlbuilder\/lib\/XMLAttribute.js:14:15)\n    
at XMLElement.module.exports.XMLElement.attribute (\/Users\/bobmayo56\/Documents\/MayoPrivate\/proj\/kkbobquery\/web\/node_modules\/simple-odata-server\/node_modules\/xmlbuilder\/lib\/XMLElement.js:82:35)\n    
at XMLElement.module.exports.XMLNode.element (\/Users\/bobmayo56\/Documents\/MayoPrivate\/proj\/kkbobquery\/web\/node_modules\/simple-odata-server\/node_modules\/xmlbuilder\/lib\/XMLNode.js:79:30)\n    
at XMLElement.module.exports.XMLNode.element (\/Users\/bobmayo56\/Documents\/MayoPrivate\/proj\/kkbobquery\/web\/node_modules\/simple-odata-server\/node_modules\/xmlbuilder\/lib\/XMLNode.js:87:25)\n    
at XMLElement.module.exports.XMLNode.element (\/Users\/bobmayo56\/Documents\/MayoPrivate\/proj\/kkbobquery\/web\/node_modules\/simple-odata-server\/node_modules\/xmlbuilder\/lib\/XMLNode.js:64:28)\n    
at XMLElement.module.exports.XMLNode.element (\/Users\/bobmayo56\/Documents\/MayoPrivate\/proj\/kkbobquery\/web\/node_modules\/simple-odata-server\/node_modules\/xmlbuilder\/lib\/XMLNode.js:84:32)\n    
at XMLElement.module.exports.XMLNode.element (\/Users\/bobmayo56\/Documents\/MayoPrivate\/proj\/kkbobquery\/web\/node_modules\/simple-odata-server\/node_modules\/xmlbuilder\/lib\/XMLNode.js:87:25)\n    
at XMLElement.module.exports.XMLNode.element (\/Users\/bobmayo56\/Documents\/MayoPrivate\/proj\/kkbobquery\/web\/node_modules\/simple-odata-server\/node_modules\/xmlbuilder\/lib\/XMLNode.js:64:28)\n    
at XMLElement.module.exports.XMLNode.element (\/Users\/bobmayo56\/Documents\/MayoPrivate\/proj\/kkbobquery\/web\/node_modules\/simple-odata-server\/node_modules\/xmlbuilder\/lib\/XMLNode.js:84:32)\n    
at XMLElement.module.exports.XMLNode.element (\/Users\/bobmayo56\/Documents\/MayoPrivate\/proj\/kkbobquery\/web\/node_modules\/simple-odata-server\/node_modules\/xmlbuilder\/lib\/XMLNode.js:87:25)",
    "target": "\/$metadata",
    "details": [

    ]
  },
  "innererror": {

  }
}

unexpected result for $filter by _id

Hello,
Can you please explain they need of code line,

 if (Object.prototype.hasOwnProperty.call(queryOptions.$filter, '_id')) {
      sAdditionIntoContext = sAdditionIntoContext.length > 0 ? '(' + sAdditionIntoContext + ')/$entity' : '/$entity'
      out['@odata.context'] = cfg.serviceUrl + '/$metadata#' + req.params.collection + sAdditionIntoContext
      if (result.length > 0) {
        for (const key in result[0]) {
          out[key] = result[0][key]
        }

I check in the OData docs, but I don't see the need of this. Can you please explain the reason behind this logic ?

left.value as canonical path generates object with items not in input order?[partial code?]

The following code separates the input filter value $filter=(location/address/firstname eq 'John') into an object.
The object results with the '/' sepated items in no alphabetical order, misrepresenting the original order.
Is it a partial code?

Node.prototype._prop = function (result, left, rightValue) {
if (left.type === 'property' && left.name.indexOf('/') !== -1) {
const fragments = left.name.split('/')
const obj = result[fragments[0]] || {}

for (let i = 1; i < fragments.length; i++) {
  if (i === (fragments.length - 1)) {
    obj[fragments[i]] = rightValue
  } else {
    obj[fragments[i]] = obj[fragments[i]] || {}
  }
}

result[fragments[0]] = obj

} else {
result[left.name] = rightValue
}
}

$inlinecount support

It seem that $inlinecount is supported, but using it the query does not return any data. still investigating, but it seem that $inlinecount is managed like $count that does not return anything but a number. at the end even the number is lost

Only accept GET

Hi,

first of all, great work! It helped me a lot for some simple data acquisition methods. I need to implement more customized POST which unfortunately use the same resource path. Is it possible to restrict the server to accept GET commands only (e.g. app.get(), app.post() in express)?

Thanks a lot.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.