0.0.8-somata-peer • Published 5 years ago

pronto-data-service v0.0.8-somata-peer

Weekly downloads
87
License
-
Repository
github
Last release
5 years ago

data-service

A base Data Service with multiple DB backends and a GraphQL layer

Installation

$ npm install git+ssh://git@github.com/prontotype-us/data-service.git

Schema

Create a file schema.sch

TODO: Document schema syntax

Usage

A DataService is used like a regular Somata Service, with a slightly different set of arguments:

new DataService(service_name, db_options, custom_methods)

DataService = require 'data-service'

# Create a MongoDB data service with only generic methods
new DataService 'myproject:data', {
    type: 'mongo'
    config: db: 'myprojectdb'
}

# Create a LocalDB with a specific method
new DataService 'myproject:data', {}, {doSpecificThing}

Options

  • type: Supported types are mongo for MongoDB, and pg for Postgres. Leave blank for an in-memory DB.
  • config:
    • id_key: Default is always id (for MongoDB, IDs are coerced from _id).
    • Specific DB types might use extra config options:
      • db: Required for MongoDB and Postgres.
      • host: Optional for MongoDB and Postgres.
      • user: Optional for MongoDB and Postgres.
    • strict_auth: Optionally set authorization to default to Unauthorized without access rules

Database Types

  • MongoDb
  • PostgresDb
  • LocalDb: In memory database for quick testing
  • ServiceDb: Forwards methods to a service that follows the data service protocol
  • MultiDb: Combines multiple Dbs into one, each Db handling its own set of collections

Methods

The base database exposes the following functions, all requiring a type argument (the collection name, e.g. "users"):

  • get(type, query) Item
  • find(type, query, search, options) {items: [Item], total: Int, pages: Int}
  • create(type, new_item, options) Item
  • update(type, id, item_update) Item
  • remove(type, id) Bool

GraphQL methods

  • query(query, context...)

The base query method takes a GraphQL query that may represent a get, find, create, or update, and one or more objects of arguments to pass to the query.

Get

Get a single item with either id or query argument:

query($id: ID) {
    user(id: $id) {id, name, email}
}
query($email: String) {
    user(query: {email: $email}) {id, name, email}
}

Find

Find multiple items, paginated, with a query. Note that the items are wrapped in another object with the shape {items, total, pages}. You can pass an options with {page} for pagination:

query($city: String, $page: Int) {
    users(query: {city: $city}, options: {page: $page}) {items {id, name, email}, total}
}

Create

Mutation query with argument create, which returns the created item:

mutation($name: String, $email: String) {
    create_user(create: {name: $name, email: $email}) {id, name, email}
}

Update

Mutation query with arguments id and update, which returns the updated item:

mutation($id: ID, $name: String) {
    update_user(id: $id, update: {name: $name}) {id, name}
}

Subscriptions

By passing a GraphQL query as second subscription argument you can specify the shape of the event to be returned. The query needs an id argument which will use the created or updated item's id.

task_query = '''
query($id: ID){
    task(id: $id){id, name}
}
'''

client.subscribe 'sconce:data', 'tasks:5:updated', task_query, ({task}) ->
    console.log '[specific task updated, graphql]', task

Without a query, you'll get the object as it is returned from the Db class.

client.subscribe 'sconce:data', 'tasks:created', (task) ->
    console.log '[any task created, raw]', task

Triggers

Attach extra actions before and after creates and updates using triggers, passed as a config argument in the shape {collection: {postCreate: fn, ...}, ...}. Available actions are preCreate, postCreate, and postUpdate.

triggers = {
    tasks: {
        postCreate: (created_task) ->
            console.log 'Look at this task', created_task
    }
}

Authorization

Optionally configure authorization for each type with three dictionaries. By default users will be authorized to perform all actions.

canUpdate = {
    '[type]': (user_id, item_id, cb) ->
        authorized = true # Bool
        cb err, authorized
}
canCreate = {
    '[type]': (user_id, new_item, cb) ->
        cb err, true
}
canRead = {
    '[type]': (user_id, item, cb) ->
        cb err, true
}

You can set the authorization to default to Unauthorized unless a successful access rule is specified with config.strict_auth = true.

The service will then offer an authorized version of each generic method, with a user_id as an additional first argument.

createAs = (user_id, args...) ->
    # optional type-wise canCreate
    @create args...

getAs = (user_id, args...) ->
    # optional type-wise canRead
    @get args...

findAs = (user_id, args...) ->
    # optional type-wise canRead
    @find args...

updateAs = (user_id, args...) ->
    # optional type-wise canUpdate
    @update args...

queryAs = (user_id, query, context) ->
    # substitute getAs, findAs for get, find...
    query query, context

# Create a LocalDB with some triggers and access rules
new DataService 'myproject:data', {
    config: {
        triggers
        canRead:
            tasks: (user_id, item, cb) ->
                get 'users', {id: user_id}, (err, user) ->
                    cb err, user.god == true ||
                        user_id in item.assigned_user_ids
        canUpdate:
            tasks: (user_id, item_id, cb) ->
                get 'users', {id: user_id}, (err, user) ->
                    cb err, user.god
    }
}

Db Subclasses

Implement these methods:

Db = require './db'

class CustomDb extends Db
    _get: (type, query, cb) ->
    _find: (type, query, search, options, cb) ->
    _findWithArray: (type, queries, cb) ->
    _create: (type, new_item, cb) ->
    _update: (type, id, item_update, cb) ->
    _remove: (type, id, cb) ->
0.1.2

5 years ago

0.2.2

6 years ago

0.2.1

6 years ago

0.2.0

6 years ago

0.0.21

6 years ago

0.0.20

6 years ago

0.0.19

6 years ago

0.0.18

6 years ago

0.0.17

6 years ago

0.0.16

6 years ago

0.0.15

6 years ago

0.0.14

6 years ago

0.0.13

6 years ago

0.0.12

6 years ago

0.0.11

6 years ago

0.0.10

6 years ago

0.0.9

6 years ago

0.1.1

6 years ago

0.1.0

6 years ago

0.0.8

6 years ago