Code Monkey home page Code Monkey logo

fortune's Introduction

Fortune.js

GitHub Actions Workflow Status npm Version License

Fortune.js is a non-native graph database abstraction layer that implements graph-like features on the application-level for Node.js and web browsers. It provides a common interface for databases, as well as relationships, inverse updates, referential integrity, which are built upon assumptions in the data model.

It's particularly useful for:

  • Bi-directional relationships in any database.
  • Applications that need storage options to be portable.
  • Sharing the same data models on the server and client.

View the website for documentation. Get it from npm:

$ npm install fortune --save

This is the core module. Additional features such as networking (HTTP, WebSocket), database adapters, serialization formats are listed in the plugins page.

Usage

Only record type definitions need to be provided. These definitions describe what data types may belong on a record and what relationships they may have, for which Fortune.js does inverse updates and maintains referential integrity. Here's an example of a basic micro-blogging service:

const fortune = require('fortune') // Works in web browsers, too.

const store = fortune({
  user: {
    name: String,

    // Following and followers are inversely related (many-to-many).
    following: [ Array('user'), 'followers' ],
    followers: [ Array('user'), 'following' ],

    // Many-to-one relationship of user posts to post author.
    posts: [ Array('post'), 'author' ]
  },
  post: {
    message: String,

    // One-to-many relationship of post author to user posts.
    author: [ 'user', 'posts' ]
  }
})

Note that the primary key id is reserved, so there is no need to specify this. Links are ids that are maintained internally at the application-level by Fortune.js, and are always denormalized so that every link has a back-link. What this also means is that changes in a record will affect the links in related records.

By default, the data is persisted in memory (and IndexedDB for the browser). There are adapters for databases such as MongoDB, Postgres, and NeDB. See the plugins page for more details.

Fortune has 4 main methods: find, create, update, & delete, which correspond to CRUD. The method signatures are as follows:

// The first argument `type` is always required. The optional `include`
// argument is used for finding related records in the same request and is
// documented in the `request` method, and the optional `meta` is specific to
// the adapter. All methods return promises.
store.find(type, ids, options, include, meta)
store.create(type, records, include, meta) // Records required.
store.update(type, updates, include, meta) // Updates required.
store.delete(type, ids, include, meta)

// For example...
store.find('user', 123).then(results => { ... })

The first method call to interact with the database will trigger a connection to the data store, and it returns the result as a Promise. The specific methods wrap around the more general request method, see the API documentation for request.

Input and Output Hooks

I/O hooks isolate business logic, and are part of what makes the interface reusable across different protocols. An input and output hook function may be defined per record type. Hook functions accept at least two arguments, the context object, the record, and optionally the update object for an update request. The method of an input hook may be any method except find, and an output hook may be applied on all methods.

An input hook function may optionally return or resolve a value to determine what gets persisted, and it is safe to mutate any of its arguments. The returned or resolved value must be the record if it's a create request, the update if it's an update request, or anything (or simply null) if it's a delete request. For example, an input hook function for a record may look like this:

function input (context, record, update) {
  switch (context.request.method) {
    // If it's a create request, return the record.
    case 'create': return record

    // If it's an update request, return the update.
    case 'update': return update

    // If it's a delete request, the return value doesn't matter.
    case 'delete': return null
  }
}

An output hook function may optionally return or resolve a record, and it is safe to mutate any of its arguments.

function output (context, record) {
  record.accessedAt = new Date()
  return record
}

Based on whether or not the resolved record is different from what was passed in, serializers may decide not to show the resolved record of the output hook for update and delete requests.

Hooks for a record type may be defined as follows:

const store = fortune({
  user: { ... }
}, {
  hooks: {
    // Hook functions must be defined in order: input first, output last.
    user: [ input, output ]
  }
})

Networking

There is a HTTP listener implementation, which returns a Node.js request listener that may be composed within larger applications. It maps Fortune requests and responses to the HTTP protocol automatically:

// Bring your own HTTP! This makes it easier to add SSL and allows the user to
// choose between different HTTP implementations, such as HTTP/2.
const http = require('http')
const fortune = require('fortune')
const fortuneHTTP = require('fortune-http')

const store = fortune(...)

// The `fortuneHTTP` function returns a listener function which does
// content negotiation, and maps the internal response to a HTTP response.
const listener = fortuneHTTP(store)
const server = http.createServer((request, response) =>
  listener(request, response)
  .catch(error => { /* error logging */ }))

store.connect().then(() => server.listen(1337))

This yields an ad hoc JSON over HTTP API, as well as a HTML interface for humans. There are also serializers for Micro API (JSON-LD) and JSON API.

Fortune.js implements its own wire protocol based on WebSocket and MessagePack, which is useful for soft real-time applications.

Features and Non-Features

  • Inverse relationship updates, automatically maintain both sides of relationships between records.
  • Referential integrity, ensure that links must be valid at the application level.
  • Type validations, fields are guaranteed to belong to a single type.
  • Adapter interface, use any database that can implement an adapter.
  • No object-relational mapping (ORM) or active record pattern, just plain data objects.
  • No coupling with network protocol, handle requests from anywhere.

License

This software is licensed under the MIT license.

fortune's People

Contributors

a0viedo avatar acoreyj avatar bpinney avatar cecemel avatar cooperka avatar ctcpip avatar dalefukami avatar damonoehlman avatar diogoazevedos avatar flet avatar gr0uch avatar insanity54 avatar itsluke avatar jcallaha avatar joefiorini avatar kketch avatar nickschot avatar repl-andrew-ovens avatar seancodes avatar susam-projects avatar taras avatar thewebguy avatar thibremy avatar wprater avatar zero-is-one 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  avatar  avatar  avatar  avatar  avatar

Watchers

 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

fortune's Issues

awaitConnection doesn't throw errors

I was experiencing some issues with the MongoDB adapter, where if I put in the wrong credentials, Mongoose would give an error, but it wasn't handled anywhere in Fortune. It seemed that adding connection errors to the awaitConnection promise was the logical thing to do, but in doing so I found that rejecting the awaitConnection promise doesn't have any effect on Fortune, and in fact doesn't even show up in the console.

I think this may be something to do with the RSVP module silencing thrown errors, but in any event it is a big problem to have no connection error handling in Fortune at all.

As an illustration of this problem, you can replace the awaitConnection method in the stub adapter with this one:

Adapter.prototype.awaitConnection = function() { return new RSVP.Promise(function (resolve, reject) { reject(new Error("some error")); }); };

And it never causes a hiccup.

Option to change the MIME type

The precise MIME types like application/vnd.api+json are really nice, but not all tools are too advanced. For example I have tried 3 Chrome extensions to visualize JSON responses and none of those were able to recognize it. Therefore I suggest an option to set the response MIME type.

Assert "Cannot call method 'forEach' of undefined"

Hello,

while using this software with an empty repo, copied your basic example with these dependencies:

"dependencies": {
    "fortune": "^0.2.4",
    "fortune-mongodb": "^0.2.2"
  }

app.js

var fortune = require('fortune')
  , app = fortune({
    adapter: 'mongodb',
    db: 'petstore'
  })
  .resource('person', {
    name: String,
    age: Number,
    pets: ['pet'] // "has many" relationship to pets
  })
  .resource('pet', {
    name: String,
    age: Number,
    owner: 'person' // "belongs to" relationship to a person
  })
  .listen(1337);

I'm trying to creating a resource, with this json:

{"pets": 
  {
    "name": "Dog",
    "age": 18
  }
}

The response is the following a 400 with this:

{
  "error": "Request was malformed.",
  "detail": "TypeError: Object #<Object> has no method 'forEach'"
}

And this is the trace on console:

Trace: [TypeError: Cannot call method 'forEach' of undefined]
    at sendError (/Users/<user>/Projects/PetExample/node_modules/fortune/lib/route.js:43:26)
    at createResources (/Users/<user>/Projects/PetExample/node_modules/fortune/lib/route.js:238:16)
    at /Users/<user>/Projects/PetExample/node_modules/fortune/lib/route.js:136:5
    at Layer.handle [as handle_request] (/Users/<user>/Projects/PetExample/node_modules/fortune/node_modules/express/lib/router/layer.js:76:5)
    at next (/Users/<user>/Projects/PetExample/node_modules/fortune/node_modules/express/lib/router/route.js:100:13)
    at Route.dispatch (/Users/<user>/Projects/PetExample/node_modules/fortune/node_modules/express/lib/router/route.js:81:3)
    at Layer.handle [as handle_request] (/Users/<user>/Projects/PetExample/node_modules/fortune/node_modules/express/lib/router/layer.js:76:5)
    at /Users/<user>/Projects/PetExample/node_modules/fortune/node_modules/express/lib/router/index.js:227:24
    at Function.proto.process_params (/Users/<user>/Projects/PetExample/node_modules/fortune/node_modules/express/lib/router/index.js:305:12)
    at /Users/<user>/Projects/PetExample/node_modules/fortune/node_modules/express/lib/router/index.js:221:12
TypeError: Cannot call method 'then' of undefined
    at /Users/<user>/Projects/PetExample/node_modules/fortune/lib/route.js:139:6
    at Layer.handle [as handle_request] (/Users/<user>/Projects/PetExample/node_modules/fortune/node_modules/express/lib/router/layer.js:76:5)
    at next (/Users/<user>/Projects/PetExample/node_modules/fortune/node_modules/express/lib/router/route.js:100:13)
    at Route.dispatch (/Users/<user>/Projects/PetExample/node_modules/fortune/node_modules/express/lib/router/route.js:81:3)
    at Layer.handle [as handle_request] (/Users/<user>/Projects/PetExample/node_modules/fortune/node_modules/express/lib/router/layer.js:76:5)
    at /Users/<user>/Projects/PetExample/node_modules/fortune/node_modules/express/lib/router/index.js:227:24
    at Function.proto.process_params (/Users/<user>/Projects/PetExample/node_modules/fortune/node_modules/express/lib/router/index.js:305:12)
    at /Users/<user>/Projects/PetExample/node_modules/fortune/node_modules/express/lib/router/index.js:221:12
    at Function.match_layer (/Users/<user>/Projects/PetExample/node_modules/fortune/node_modules/express/lib/router/index.js:288:3)
    at next (/Users/<user>/Projects/PetExample/node_modules/fortune/node_modules/express/lib/router/index.js:182:10)

What's I'm doing wrong?

defining layouts

Just a suggestion, I noticed that you defined the same layout for each target in the assemble task (https://github.com/daliwali/fortune/blob/gh-pages/Gruntfile.js#L74-L76).

you can specify a layout at the task-level as well:

assemble: {
  options: {
    assets: 'dist',
    helpers: 'helpers/*.js',
    layout: 'main.hbs'
    layoutdir: 'templates/layouts',
    partials: ['templates/partials/**/*.hbs'],
    data: ['docs/**/*.json'],
    marked: {
      sanitize: false,
      gfm: true,
      breaks: true
    }
  },
  home: {
    src: ['templates/index.hbs'],
    dest: 'index.html'
  },
  docs: {
    src: ['templates/docs.hbs'],
    dest: 'docs/index.html'
  },
  guide: {
    src: ['templates/guide.hbs'],
    dest: 'guide/index.html'
  },
  404: {
    src: ['templates/404.hbs'],
    dest: '404.html'
  }
},

or even just layout: 'templates/layouts/main.hbs' and get rid of layoutdir if you only intend to have one layout.

POST Content-Type application/vnd.api+json fails

POSTs with the Content-Type application/vnd.api+json fail because body-parser only accepts application/json. To fix this just add another body-parser:

   router.use(bodyParser.json({ type: 'application/*+json' }));
   router.use(bodyParser.json());

JSON API RC3 tracking

Hey there! I just wanted to let you know that JSON API has hit RC3, and we'd like client libraries to start implementing it like it was 1.0, as a final check that we're good for 1.0. I wanted to make this issue to let you know!

json-api/json-api#484

Database missing a column in POSTed JSON

I'm using Ember with @daliwali's ember-data JSON-API adapter. I have a very simple resource with 3 string fields, connecting to Mongo instance on mongohq.com. When I create a record from Ember, the request body contains a "displayName" field, and the record shows up in the Mongo collection, but it's missing the "displayName" field. Is there any reason this should be excluded?

Error messages are exception-based, and not tailored to the API end user

I was going to submit a PR to fix one instance of this but thought it might be good to have a discussion about whether this is something you want to "fix" first, and how to best do it across the board.

In issue #41, I mentioned that I was receiving an error on POST that looks like this:

{
  "error": "Request was malformed.",
  "detail": "TypeError: Cannot call method 'forEach' of undefined"
}

I tracked it down to the sendError method, then to the fact that I had sent my POST request as one single JSON object instead of a hash with a key of "books" and a value containing an array of book objects to create. That functionality itself is kind of weird but it's being debated at the json-api level (json-api/json-api#131).

But here, since the try/catch block returns an error object that just gets sent along to the sendError method, Fortune is usually returning a standard JavaScript exception error to the user. Shouldn't API end users receive error messages that pertain to the formatting error that they made in their request, and not the specific code error that triggered the exception?

A fix for that block of code might look something like this:

try {
  req.body[collection].forEach(function(resource) {
    before.push(beforeTransform(resource, req, res));
  });
} catch(error) {
  console.error(error);
  return sendError(req, res, 400, "Resource definitions must be enclosed in a hash/array, e.g. { '" + collection + "': [ { resource definition } ] }");
}

Modularize adapters

Right now the package needs mongoose and orm in order to work, but if you are using the default adapter you don't need orm support and vice versa. And for someone like me who doesn't have any of them the module just fails to start:

events.js:71
        throw arguments[1]; // Unhandled 'error' event
                       ^
Error: failed to connect to [localhost:27017]
    at Server.connect.connectionPool.on.server._serverState (/home/alejandro/code/fnz/node_modules/fortune/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/server.js:540:74)
    at EventEmitter.emit (events.js:126:20)
    at connection.on._self._poolState (/home/alejandro/code/fnz/node_modules/fortune/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/connection_pool.js:140:15)
    at EventEmitter.emit (events.js:99:17)
    at Socket.errorHandler (/home/alejandro/code/fnz/node_modules/fortune/node_modules/mongoose/node_modules/mongodb/lib/mongodb/connection/connection.js:478:10)
    at Socket.EventEmitter.emit (events.js:96:17)
    at Socket._destroy.self.errorEmitted (net.js:329:14)
    at process.startup.processNextTick.process._tickCallback (node.js:245:9)

So, I think is a good idea to modularize the custom adapters and with this you can let the user to choose whatever he/she wants. Also with this we'll be able to create custom adapters for more databases or clients. e.g:

var fortune = require('fortune');
var couch = require('fortune-couch');

var driver = fortune({
  adapter: couch
});

cheers

unify the query object

The adapter.find and adapter.findMany methods should have the same public interface for arbitrary queries.

Customize CORS Headers

Any of them should be customizable, but specifically I need to customize headers to pass additional authorization credentials. I'm thinking of implementing something like this:

fortune({
  db: "...",
  host: "...",
  cors: {
    headers: ["X-SignedInAs"],
    origins: ["localhost:3000"/*, ... */],
    methods: ["POST", "PUT"],
    credentials: false
  }
})
//...

If a user chooses not to customize any of these options, they could just pass "true" or "false" as they can today. The headers would default to the same values currently hardcoded in the allowCrossDomain middleware.

What do you think of this? Would you accept a PR for such customization?

API Migrations

At the moment, if you change a field name in the API schema, the old field is ignored in new API requests.

It might be a better approach to just return the entire document, instead of only what's in the schema.

Function .noIndex() is not working

Consider the following application:

var fortune = require('fortune');
var app = fortune({
    db: 'phonebook'    
});

app.resource('entry',{
    name: String,
    telephone: String
})
.noIndex()

.listen(process.env.PORT);

The /entries endpoint is working. According the docs, it shouldn't.
Tried .noIndex('entry'), still the same.

POST fails on a very simple scenario

I am trying out Fortune.js for the first time and I wrote a very simple app that doesn't seem to work.

The server:

var fortune = require('fortune');
var options = {
  db: 'demo_app'
};
server = fortune(options).
resource("project", {
    name: String
});

The client (ember):

App.Project = DS.Model.extend({
    name: DS.attr("string")
});

var project = this.store.createRecord("project", {
            name: "This is a test project!"
});
project.save();

The request payload:

{"project":{"name":"This is a test project!"}}

The server exception:

[TypeError: Cannot call method 'forEach' of undefined]
    at sendError (d:\repos\openquip\node_modules\fortune\lib\route.js:43:26)
    at createResources (d:\repos\openquip\node_modules\fortune\lib\route.js:238:

What did I do wrong here?

Create new adapter during runtime

Hi All,

Is it possible to change the adapter during runtime? For example having a resource available at ~/resource1, but after calling a function change for the adapter to point at ~/resource2.

Is this possible or would a new instance of Fortune need to be created?

Cheers

Scott

expose fortune as middleware

decoupling fortune from being only a server to middleware means it will be usable across far more scenarios.

As it is now, I can't use fortune since I'm forced to use an existing http.Server

filtering, sorting, pagination

Query-string syntax to filter, sort, and paginate. Example:

?filter={"name":"daliwali"}&sort={"age":1}&page=1

The findMany method in the adapter already supports finding resources by arbitrary queries, but pagination might be tricky.

Can't filter a resource

I have a simple resource defined as follows:

...
.resource('event', {
    time: Date, 
    subject_id:String,
    user_id:String,
    type: String
  })
...

I can GET
http://localhost:8090/events
but filtering does not seem to be happening:

http://localhost:8090/events?type=typeA
http://localhost:8090/events?subject_id=2
http://localhost:8090/events?user_id=abc
These all return the full list of events, even though these attribute values only apply to a subset of the data.

How is filtering supposed to work with fortune? Or is it broken/not implemented? I notice JSON API spec says it is optional.

Create related object with main object

Hi all

Is it possible to generate an owner and his child in one post request?
Let's assume you use fortune.js for a customer db and you want to create a customer that owns an address. How can you accomplish this in one request so you do not have to wait for the callback of the newly generated customer id?

Thanks,
Das Einhorn

Feature: validations

Is this on the roadmap, or against your philosophy?

something like

  app.resource('person', {
    email: {type: String, validates: 'email'}
  });

where one could register validation extensions to Fortune via fortune.addValidation('email', function(before, after) { .. }) or something similar?

Incomplete implementation of RFC 6902

Is there a reason why only the "replace" operation of RFC 6902 was implemented in Fortune? The lack of add is preventing many-to-many operations from being possible.

If I have two users and one group and add both users to the group, the users each belong to one group and the group has two users. This is one-to-many.

If I create another group and try to add that group to one of the users, the new group should be added to to the specified user. Basically, we'd end up with the following: many-to-many relationship:

User A
....Group A
....Group B
User B
....Group A
Group A
....User A
....User B
Group B
....User A

Instead, with only "replace", the PATCH will replace the old group for that user and each user will end up belonging to one group (different groups) and each group has one user. i.e.

User A
....Group B
User B
....Group A
Group A
....User B
Group B
....User A

What I need to be able to do is PATCH add.

At this very moment I am looking at the code trying to determine the best way to create support for the "add" and "remove" operations of RFC 6902. If you or anyone else has already looked into this, I'd sincerely appreciate knowing about any gotchas or algorithmic limitations sooner than later. Thanks!

Relations between resources

Me again for a silly question.
I am trying to build a maze using hypermedia API.

That's my resources

app.resource('maze',{
    name: String,
    cells: ['cell'],
    start: {ref: 'cell'}
});

app.resource('cell',{
    name: String,
    north: {ref:'cell'},
    east: {ref:'cell'},
    south: {ref:'cell'},
    west: {ref:'cell'},
    maze: {ref: 'maze'},
});

When I do a patch with (replace) on one cell to tell which other cell is at north it works great.
I got as expected

{
            "id": "Ol4MkfL75QFcweUR",
            "name": "Spoon Storage",
            "links": {
                "north": "KcicJj811BQuF0dF",
                "maze": "1gtV4qx4qdiKvVE3"
            }
        },

But the one that was north, and where nothing should have happened is getting updated too..

{
            "id": "KcicJj811BQuF0dF",
            "name": "Banquet Hall",
            "links": {
                "north": "Ol4MkfL75QFcweUR",
                "east": "Ol4MkfL75QFcweUR",
                "south": "Ol4MkfL75QFcweUR",
                "west": "Ol4MkfL75QFcweUR",
                "maze": "1gtV4qx4qdiKvVE3"
            }
        },

How could I say that north, east,south,west are links to a cell object, but to different cell objects ?

Thanks :)

Support easier contributions for Windows users

Would you accept a pull request that transitioned the stuff that's currently done in the Makefile to use Gulp? Alternatively, I could write a node script that would execute the same commands as make, but in a cross platform manner.

Currently you can't actually build the project on Windows easily due to command and path separator differences. In order to get it working, I ended up installing a bunch of GnuWin32 utilities and modifying the Makefile

CORS enabling not working?

Not sure if I am doing something wrong, but I can't seem to enable CORS.

My fortune API looks like this (CoffeeScript/Node.JS):

argv = require("minimist")(process.argv.slice(2))
API = require("fortune")
if argv._.length == 0
  console.log "Usage: node API.js <port>"
else
  port = argv['_'][0]
  app = API(db: "user-event-log", cors:true).resource("event",
    time: Date
    subject_id: String
    user_id: String
    type: String
  ).listen(port)
  exports.API = API

My dependencies from package.json are:

  "dependencies": {
    "coffee-script": "^1.8.0",
    "fortune": "^0.2.4",
    "i": "^0.3.2",
    "minimist": "^1.1.0"
  },

When I launch with node API.js 8080 and GET http://localhost:8090/events, I do not see the Access-Control-Allow-Origin:* header in the response:

Date: Fri, 30 Jan 2015 16:22:02 GMT 
Content-Type: application/vnd.api+json; charset=utf-8 
Content-Length: 378 
ETag: W/"17a-679725808" 

Am I doing something wrong or is this a bug?

Adapter.toJSON()

Resources could really use a way of getting all of the data at once. findMany is a bit confusing to use, I can't figure out how to get all the data for a specific resource.

myResource.adapter.toJSON() would work nicely.

promise api is awkward, an prone to developer error.

current the api is as follows:

function(resolve, reject, request) {
  var authorization = request.get('Authorization');
   if(!authorization) return reject();
   this.authorization = authorization;
   resolve(this);
}

Proposed api:

function(request) {
  var authorization = request.get('Authorization');
   if(!authorization) throw new Error('Authorization Failed');
   this.authorization = authorization;
   return this;
}

callback callsite:

new RSVP.Promise(function(resolve){
  resolve(callback(args));
});

Why this proposal?

  1. lets use return/throw as if no asynchrony exists.
  2. if you want to deal with non-future values, you simply return, just as is natural (no knowledge of resolve/reject is needed)
  3. if you deal with future values, simply return a promise (which will settle in the future)
  4. if a exception is thrown in the callback, it gets handled like any other rejection, and correctly propagates, informing the caller in the same manner as a rejection.
  5. as promises always settle asynchronously, one can assume with confidence ordering remains consistent.

Multiple before and after filters

Have you thought about this? I was thinking about a PR, but wanted to see if there were any specific reasons why you'd not implemented this.

Would be nice to apple filters like so:

  app.resource('user', {

    name: String,
    createdAt: Date,
    updatedAt: Date,
    password: String,
    salt: Buffer,
    tokens: ['token']

  })
  .before(timestamp)
  .before(defaults)
  .before(createOrUpdate.before)
  .after(createOrUpdate.after);

Adapter find function

Hi,

I am trying to use the Adapter.find() function to retrieve a particular object in my database.

in my code I do

var result = app.adapter.find('cell',"ZnqcWstX5rx9rKMT");
console.log("RESULT",result);

but I got

{ _id: 4,
  _label: undefined,
  _subscribers: [],
  _state: 0,
  _detail: [TypeError: Cannot call method 'findOne' of undefined] }

and when I do a console.log(app.adapter) before it seems defined.

Any idea how to solve this or what I am doing wrong?
Thanks

Rewrite volunteers

I am looking to be involved in rewrite. Is there anything I can do to help fortune move faster?

ID style not showing links

Is there a reason why the ID style is not showing my one-to-many links? I realize the specs say the links MAY be shown, but how else does one get those relations; exposing them manually in the after transform?

Typo in guide?

In the Guide you have

app.resource('person', {
  name: String,
  spouse: {ref: 'person', inverse: 'spouse'},
  lovers: [{ref: 'person', inverse: 'lovers'}]
});
app.after('person', function(request, response) {
  delete this.links.lovers;
  return this;
});

Shouldn't that be

  delete this.lovers;

(i.e. no ".links")?

Thanks.
-Shaun

request: provide means to access http.Server object

There doesn't seem to be a way to get access to the http.Server object for a given Fortune instance. This can be handy when access to the methods for http.Server or net.Server is desired.

A comment in the Express code explains how you might gain access to the server object (in a pure-Express app):

var http = require('http')
  , https = require('https')
  , express = require('express')
  , app = express();
http.createServer(app).listen(80);
https.createServer({ ... }, app).listen(443);

The listen method on a pure-Express app also returns the http.Server object, for convenience.

Fortune's listen method is chainable (returning this) so this probably wouldn't be the approach for this project.

Perhaps the http.Server object could be saved to app.server, just as app.router stores a reference to the Express instance?

In the meantime, we could explicitly call server = app.router.listen(80), but it would be nice if there was convenient and official access to it somewhere.

Definitely loving Fortune so far. I think Node.JS has needed something like Fortune for a long time. <3

Option to append `.json` to routes

If I want to serve my posts form myapp.com/posts, then shouldn't I be able to serve the API from myapp.com/posts.json?

Is this frowned upon? Does this go against the spirit of Fortune.js?

relational database associations

Currently, a join table is created for a bi-directional many-to-one relationship, which is not necessary or compatible with most ORMs.

Also, a one-to-one relationship maintains a foreign key on both objects, which may not be necessary on most ORMs.

Enhancement: Method for accessing stored procedures

It would be fantastic if fortune allowed for a stored procedure to be accessed in addition to direct table access.

For instance I have databases that I can run a function like:

SELECT column1, column2 FROM my_db_side_function('value1', 'value2);

The function could return a dataset like a table, or a return value. I am not sure how this would be implemented, but if fortune could handle this it would be my ideal situation.

Specify ID when creating new entry

Is it possible to specify an ID when POSTing a new entry to the DB? The potential usecase is loading sample data for testing to the DB via the web services themselves.

'Request was malformed' on POST, error is hard to track down in the code

Just trying to get fortune up and running with a simple test, with this:

var fortune = require('fortune');

fortune()
  .resource('book', {
    title: String
  })
  .listen('3000');

Which is working fine listening, and /books returns { "books": [ ] } as expected. But when I try to POST to create a new book, like this:

$ curl -X POST -H "Content-Type: application/json" -d '{"title":"A Storm of Swords"}' http://localhost:3000/books

I get the following, somewhat unspecific error.

{
  "error": "Request was malformed.",
  "detail": "TypeError: Cannot call method 'forEach' of undefined"
}
  1. It would be nice if that error was a little more specific, or gave me some way to track down where it came from. Even a search through my entire app for "Cannot call method" returns no results so I'm having trouble tracking it down.
  2. Is this an error in my POST request or a bug in the library?

Thanks!

Fortune crashes on post (postman form-data)

I'm trying to do a post via postman and if the form-data tab in postman is selected fortune crashes and I get this in the terminal TypeError: Cannot read property 'split' of undefined.

My app looks like this.

var fortune = require('fortune');

options = {
  adapter: 'nedb',
  host: 'localhost',
  flags: {
    inMemoryOnly: false,
    filename: 'data.db',
    autoload: true
  }
};

var app = fortune(options);

app.resource('item',{
  name: String
});

app.listen(1337);

The database is being created when I post.

What am I doing wrong and how would I do a post in postman?
Thanks.

Method to enumerate routes

A method to get the current list of valid routes would be useful for documentation purposes, especially when the API is to be exposed to a public audience.

This would also allow users to build documentation such as Swagger or RestDoc around the API.

Error on POST. How should I do it ?

Hi, I have this resources definition:

/**
 * Created by aldo on 5/4/14.
 */
var fortune = require('fortune')
  , express = fortune.express;

var container = express()
  , port = process.argv[2] || 1337;

var app = fortune({
  db: 'fortune-admin',
  adapter: 'mongodb',
  namespace: '/api/v1'
})

.resource('user', {
  title : String,
  firstName : String,
  lastName : String,
  role : String,
  email : String,
  nationality: String,
  languageCode: String,
  addresses: ['address']
}).transform(
  function (request) {
    console.log(request.body);
  }
)

.resource('address', {
  type: String,
  addressLine1: String,
  addressLine2: String,
  addressLine3: String,
  addressLine4: String,
  city: String,
  region: String,
  postCode: String,
  country: String,
  dateDeleted: Date,
  user: { ref: "user", inverse: "addresses" }
});

container
  .use(express.static(__dirname + '/public/app'))
  .use(express.static(__dirname + '/public/app/scripts'))
  .get('*', function(req, res) {
    res.send('Hello, you have reached the API.');
  })
  .use(app.router)
  .listen(port);

console.log('Listening on port ' + port + '...');

and I'm getting an error making a POST

Listening on port 1337...
{ users:
   [ { title: 1,
       firstName: 'Foo',
       lastName: 'Bar',
       role: 2,
       email: 'foo@bar.com',
       languageCode: 'en',
       _id: 1 } ] }
Trace: [TypeError: Cannot call method 'map' of undefined]
    at sendError (/Users/aldo/Satio/Desarrollo/node/fortune-admin/node_modules/fortune/lib/route.js:43:26)
    at createResources.then.then.body (/Users/aldo/Satio/Desarrollo/node/fortune-admin/node_modules/fortune/lib/route.js:180:7)
    at invokeCallback (/Users/aldo/Satio/Desarrollo/node/fortune-admin/node_modules/fortune/node_modules/rsvp/dist/commonjs/rsvp/promise.js:228:21)

Thanks.

unable to make memory-only NeDB

Loving FortuneJS so far. <3

I'm trying to write a repeatable test scenario, so engaging NeDB's memory-only mode would be perfect. I've tried something like this:

  options = {
    adapter: 'nedb',
    host: 'localhost',
    namespace: '/api/v1',
    flags: {
      inMemoryOnly: true,
      filename: '',
      autoload: false
    }
  };
  app = fortune(options);

Even though I'm not setting options.db, nothing seems to stop the default NeDB adapter from setting a location on disk. Is there something else I can try?

Add operation on patch does not work

Hi,

I tried to perform an "add" operation using PATCH, does not seem to work. Nothing is added.
"replace" an existing field works perfectly.
I did try to "replace" a non existing field, and it added as expected for the "add" operation.

Syntax that I used:

[{"op":"add","path":"/cells/0/title","value":"My cell"}]

Thanks for your help

I used Postman to try the request

Accessing raw request/headers in Adapters

Is there a publicly supported manner of getting access to a given request's headers somewhere such that I can access them or pass them along to a custom adapter?

My use case is that I have an adapter that will be talking to another web service rather than a database directly. To support that, I'll need to pass along authentication data for the current request to that web service

*Edit I should mention that I'm working against the rewrite branch

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.