Skip to content

Commit

Permalink
feat: open & handle sessions automatically (#10)
Browse files Browse the repository at this point in the history
* feat: open & handle sessions automatically

* chore: restyle & reuse example classes

* feat: allow sessions targeting multiple namespaces

* chore: add autoSession to examples

* chore: add autoSession in readme

* build: run ravendb container before tests

* test: add autoSession TS types

* chore: remove example uppercase, update toc

* Update lib/helpers.js

Co-authored-by: Matteo Pietro Dazzi <[email protected]>

* Update index.js

Co-authored-by: Matteo Pietro Dazzi <[email protected]>

* style: improve autoSession condition check

Co-authored-by: Matteo Pietro Dazzi <[email protected]>
  • Loading branch information
drakhart and ilteoood authored Sep 23, 2022
1 parent f273bd8 commit f1f1814
Show file tree
Hide file tree
Showing 18 changed files with 475 additions and 75 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: Continuous Integration

on:
on:
push:
branches:
- master
Expand All @@ -18,6 +18,7 @@ jobs:
- run: |
npm ci
npm run lint
npm run docker
npm test
automerge:
Expand Down
53 changes: 36 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ RavenDB plugin for Fastify. Internally it uses the official [ravendb](https://gi

- [Installation](#installation)
- [Usage](#usage)
- [Basic example](#basic-example)
- [Advanced examples](#advanced-examples)
- [Examples](#examples)
- [Name option](#name-option)
- [Docker](#docker)
- [Testing](#testing)
- [License](#license)

## Installation
Expand All @@ -23,50 +23,63 @@ npm i fastify-ravendb

## Usage

Register it as any other Fastify plugin. It will add the `rvn` (same name as RavenDB's CLI tool) namespace to your Fastify instance. You can access and use it the same as you would do with any `DocumentStore` instance from the official RavenDB Node.js client.
Register it as any other Fastify plugin. It will decorate your Fastify instance with the `rvn` (same name as RavenDB's CLI tool) object, and you can access and use it the same as you would do with any `DocumentStore` instance from the official RavenDB Node.js client.

Once the plugin is registered you can also enable automatic session handling for specific routes, via the `rvn.autoSession` route option. Sessions will be automatically open in the `onRequest` hook, requests will be decorated with the `rvn` object (the session, which you can use as with any session from the official client), and any pending changes will be saved `onResponse`.

### Plugin options

You can pass the following options when registering the plugin (all of them are optional unless stated otherwise):

| Parameter | Example | Description |
| --- | --- | --- |
| `name` | `db1` | Specific name for the `DocumentStore` instance. Please check [Name option](#name-option) for more information.
| `url` (required) | `http://live-test.ravendb.net` | RavenDB server URL. Same as in [ravendb#getting-started](https://github.com/ravendb/ravendb-nodejs-client#getting-started).
| `databaseName` (required) | `test` | Database name. Same as in [ravendb#getting-started](https://github.com/ravendb/ravendb-nodejs-client#getting-started).
| `name` | `'db1'` | Specific name for the `DocumentStore` instance. Please check [Name option](#name-option) for more information.
| `url` (required) | `'http://live-test.ravendb.net'` | RavenDB server URL. Same as in [ravendb#getting-started](https://github.com/ravendb/ravendb-nodejs-client#getting-started).
| `databaseName` (required) | `'test'` | Database name. Same as in [ravendb#getting-started](https://github.com/ravendb/ravendb-nodejs-client#getting-started).
| `authOptions` | `{ certificate: fs.readFileSync(certificate), type: 'pem' }` | Authentication options (i.e. certificate and password). Same as in [ravendb#working-with-secured-server](https://github.com/ravendb/ravendb-nodejs-client#working-with-secured-server).
| `findCollectionNameForObjectLiteral` | `e => e._collection` | A function to extract the target collection from an object literal entity. Same as in [ravendb#using-object-literals-for-entities](https://github.com/ravendb/ravendb-nodejs-client#using-object-literals-for-entities).

## Basic example
### Route options

You can pass these options when creating routes (all of them are optional):

| Parameter | Examples | Description |
| --- | --- | --- |
| `rvn.autoSession` | `true` \| `'test'` \| `['local', 'external']` | Whether to open sessions automatically for specific database instances. It can be a boolean to target the global instance, or a string/array of strings to target one or multiple named instances. Please check [Name option](#name-option) for more information. |

## Examples

### Basic example

```javascript
import Fastify from 'fastify'
import plugin from 'fastify-ravendb'

export class Person {
class Person {
constructor(name) {
this.name = name
}
}

const routeOptions = { rvn: { autoSession: true } }

const start = async () => {
const fastify = Fastify({ logger: true })
await fastify.register(plugin, {
url: 'http://live-test.ravendb.net/',
databaseName: 'test'
})

fastify.post('/people', async (req, reply) => {
fastify.post('/people', routeOptions, async (req, reply) => {
const person = new Person(req.body.name)

const session = fastify.rvn.openSession()
await session.store(person, Person)
await session.saveChanges()
await req.rvn.store(person)

reply.send(person)
})

fastify.get('/people/:id', async (req, reply) => {
const session = fastify.rvn.openSession()
const person = await session.load(`people/${req.params.id}`, Person)
fastify.get('/people/:id', routeOptions, async (req, reply) => {
const person = await req.rvn.load(`people/${req.params.id}`, Person)

reply.send(person)
})
Expand All @@ -77,7 +90,7 @@ const start = async () => {
start()
```

## Advanced examples
### Advanced examples

More advanced examples are provided in the [examples folder](examples/).

Expand Down Expand Up @@ -105,7 +118,7 @@ await fastify.register(plugin, {

## Docker

A RavenDB Docker container with a database fixture named `test` has been provided for convenience and for using the advanced examples.
A RavenDB Docker container with a database fixture named `test` has been provided for running tests which require a real database and for using the advanced examples.

### Run

Expand Down Expand Up @@ -157,6 +170,12 @@ If port `8080` is already allocated in your system, you can change the `config.d

If at some point you want to reset the fixtures you can remove the container and start again.

## Testing

In order to run the tests first you'll need to start the provided Docker container. Please check [Docker](#docker) for more information.

While the container is up you can run the tests with `npm test`.

## License

Copyright NearForm Ltd. Licensed under the [Apache-2.0 license](http://www.apache.org/licenses/LICENSE-2.0).
12 changes: 12 additions & 0 deletions examples/lib/classes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export class Category {
constructor(name, description) {
this.name = name
this.description = description
}
}

export class Person {
constructor(name) {
this.name = name
}
}
2 changes: 1 addition & 1 deletion examples/constants.js → examples/lib/constants.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { readFileSync } from 'fs'

// Workaround for ESLint not being able to parse "assert { type: 'json' }" when importing modules
const pjsonUrl = new URL('../package.json', import.meta.url)
const pjsonUrl = new URL('../../package.json', import.meta.url)
const pjson = JSON.parse(readFileSync(pjsonUrl))

export const port = 3000
Expand Down
55 changes: 55 additions & 0 deletions examples/multiple-dbs-with-session.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import Fastify from 'fastify'

import plugin from '../index.js'
import { Category, Person } from './lib/classes.js'
import { port, url, databaseName, categories, people } from './lib/constants.js'

const routeOptions = { rvn: { autoSession: ['local', 'remote'] } }

const start = async () => {
const fastify = Fastify({ logger: true })
await fastify.register(plugin, { name: 'local', url, databaseName })
await fastify.register(plugin, {
name: 'remote',
url: 'http://live-test.ravendb.net/',
databaseName
})

fastify.post(`/${categories}`, routeOptions, async (req, reply) => {
const category = new Category(req.body.name, req.body.description)

await req.rvn.remote.store(category)

reply.send(category)
})

fastify.get(`/${categories}/:id`, routeOptions, async (req, reply) => {
const category = await req.rvn.remote.load(
`${categories}/${req.params.id}`,
Category
)

reply.send(category)
})

fastify.post(`/${people}`, routeOptions, async (req, reply) => {
const person = new Person(req.body.name)

await req.rvn.local.store(person)

reply.send(person)
})

fastify.get(`/${people}/:id`, routeOptions, async (req, reply) => {
const person = await req.rvn.local.load(
`${people}/${req.params.id}`,
Person
)

reply.send(person)
})

await fastify.listen({ port })
}

start()
16 changes: 2 additions & 14 deletions examples/multiple-dbs.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,8 @@
import Fastify from 'fastify'

import plugin from '../index.js'
import { port, url, databaseName, categories, people } from './constants.js'

export class Category {
constructor(name, description) {
this.Name = name
this.Description = description
}
}

export class Person {
constructor(name) {
this.Name = name
}
}
import { Category, Person } from './lib/classes.js'
import { port, url, databaseName, categories, people } from './lib/constants.js'

const start = async () => {
const fastify = Fastify({ logger: true })
Expand Down
4 changes: 2 additions & 2 deletions examples/object-literals.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Fastify from 'fastify'

import plugin from '../index.js'
import { port, url, databaseName, people } from './constants.js'
import { port, url, databaseName, people } from './lib/constants.js'

const start = async () => {
const findCollectionNameForObjectLiteral = e => e._collection
Expand All @@ -15,7 +15,7 @@ const start = async () => {

fastify.post(`/${people}`, async (req, reply) => {
const person = {
Name: req.body.name,
name: req.body.name,
_collection: people
}

Expand Down
30 changes: 30 additions & 0 deletions examples/single-db-with-session.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Fastify from 'fastify'

import plugin from '../index.js'
import { Person } from './lib/classes.js'
import { port, url, databaseName, people } from './lib/constants.js'

const routeOptions = { rvn: { autoSession: true } }

const start = async () => {
const fastify = Fastify({ logger: true })
await fastify.register(plugin, { url, databaseName })

fastify.post(`/${people}`, routeOptions, async (req, reply) => {
const person = new Person(req.body.name)

await req.rvn.store(person)

reply.send(person)
})

fastify.get(`/${people}/:id`, routeOptions, async (req, reply) => {
const person = await req.rvn.load(`${people}/${req.params.id}`, Person)

reply.send(person)
})

await fastify.listen({ port })
}

start()
9 changes: 2 additions & 7 deletions examples/single-db.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
import Fastify from 'fastify'

import plugin from '../index.js'
import { port, url, databaseName, people } from './constants.js'

export class Person {
constructor(name) {
this.Name = name
}
}
import { Person } from './lib/classes.js'
import { port, url, databaseName, people } from './lib/constants.js'

const start = async () => {
const fastify = Fastify({ logger: true })
Expand Down
16 changes: 14 additions & 2 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { FastifyPluginCallback } from 'fastify';
import { IDocumentStore, IStoreAuthOptions } from 'ravendb'
import { IDocumentSession, IDocumentStore, IStoreAuthOptions } from 'ravendb'

type IOptions = {
/**
Expand Down Expand Up @@ -28,14 +28,26 @@ type IOptions = {
findCollectionNameForObjectLiteral?: (entity: object) => string;
}

type IRouteOptions = {
autoSession?: boolean | string | string[];
}

declare const plugin: FastifyPluginCallback<IOptions>;

declare module 'fastify' {
export interface FastifyInstance {
rvn: IDocumentStore & Record<string, IDocumentStore>;
}

export interface FastifyRequest {
rvn?: IDocumentSession & Record<string, IDocumentSession>;
}

export interface RouteShorthandOptions {
rvn?: IRouteOptions;
}
}

export { plugin, IOptions };
export { plugin, IOptions, IRouteOptions };

export default plugin;
Loading

0 comments on commit f1f1814

Please sign in to comment.