Skip to content

Commit

Permalink
feat(document datasource): add document datasource
Browse files Browse the repository at this point in the history
  • Loading branch information
danwkennedy committed May 14, 2019
1 parent ca204df commit 274a1d8
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 0 deletions.
82 changes: 82 additions & 0 deletions arango-document-datasource.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
const { DataSource } = require('apollo-datasource');
const DataLoader = require('dataloader');
const { aql } = require('arangojs');

/**
* An ArangoDb implementation of the Apollo DataSource.
*
* This DataSource is solely for resolving Arando Documents by their Id.
* Requests are batched and cached per request.
*
* @class ArangoDocumentDataSource
* @extends {DataSource}
*/
class ArangoDocumentDataSource extends DataSource {
/**
* Creates an instance of ArangoDocumentDataSource.
* @param {Database} db
* @memberof ArangoDocumentDataSource
*/
constructor(db) {
super();
this.db = db;
}

/**
* Initializes the DataSource. Called at the beginning of each request.
*
* @memberof ArangoDocumentDataSource
*/
initialize() {
this.dataloader = new DataLoader(keys => this.loadKeys(keys));
}

/**
* Gets a Document by its _id
*
* @param {string} id The _id to query for
* @returns {any} The corresponding Document
* @memberof ArangoDocumentDataSource
*/
async get(id) {
return this.dataloader.load(id);
}

/**
* Gets several Documents at once by their _id
*
* @param {string[]} ids The _ids to query for
* @returns {any[]} The corresponding Documents. In the order their Ids were specified in the ids array
* @memberof ArangoDocumentDataSource
*/
async getMany(ids) {
return this.dataloader.loadMany(ids);
}

/**
* Queries the database for the given keys.
*
* @param {string[]} keys The keys to qeury for
* @private
* @returns {*[]} The corresponding Documents. In the order their Ids were specified in the ids array
* @memberof ArangoDocumentDataSource
*/
async loadKeys(keys) {
const cursor = await this.db.query(aql`RETURN DOCUMENT(${keys})`);
const nodes = await cursor.all();

return keys.map(key => {
const node = nodes.find(node => node._id === key);

if (node) {
node.id = node._id;
}

return node;
});
}
}

module.exports = {
ArangoDocumentDataSource: ArangoDocumentDataSource
};
51 changes: 51 additions & 0 deletions arango-document-datasource.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
const { ArangoDocumentDataSource } = require('.');

describe('ArangDocumentDataSource', () => {
test('it batches key loads', async () => {
const results = [{ _id: '123' }, { _id: '456' }];
const db = createDb(results);
const datasource = new ArangoDocumentDataSource(db);
datasource.initialize();

const docs = await Promise.all([
datasource.get('123'),
datasource.get('456')
]);

expect(docs).toEqual(results);
expect(db.query).toHaveBeenCalledTimes(1);
});

test('it caches key loads', async () => {
const results = [{ _id: '123' }];
const db = createDb(results);
const datasource = new ArangoDocumentDataSource(db);
datasource.initialize();

for (let i = 0; i < 3; i++) {
const doc = await datasource.get('123');
expect(db.query).toHaveBeenCalledTimes(1);
expect(doc).toEqual(results[0]);
}
});

test('it returns docs for several keys', async () => {
const results = [{ _id: '123' }, { _id: '456' }];
const db = createDb(results);
const datasource = new ArangoDocumentDataSource(db);
datasource.initialize();

const docs = await datasource.getMany(['456', '123']);

expect(docs).toEqual(results.reverse());
expect(db.query).toHaveBeenCalledTimes(1);
});
});

function createDb(queryResults) {
return {
query: jest.fn().mockResolvedValue({
all: () => queryResults
})
};
}
4 changes: 4 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
...require('./arango-datasource'),
...require('./arango-document-datasource')
};

0 comments on commit 274a1d8

Please sign in to comment.