relay-mongoose
Advanced tools
Comparing version 0.1.0 to 0.2.0
"use strict"; | ||
var __extends = (this && this.__extends) || (function () { | ||
var extendStatics = function (d, b) { | ||
extendStatics = Object.setPrototypeOf || | ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || | ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; | ||
return extendStatics(d, b); | ||
}; | ||
return function (d, b) { | ||
extendStatics(d, b); | ||
function __() { this.constructor = d; } | ||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); | ||
}; | ||
})(); | ||
var __assign = (this && this.__assign) || function () { | ||
@@ -63,18 +50,28 @@ __assign = Object.assign || function(t) { | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.enhancedModel = void 0; | ||
var mongoose_1 = require("mongoose"); | ||
exports.EnhancedModel = exports.fromRelayId = void 0; | ||
var mongoose = require("mongoose"); | ||
exports.enhancedModel = function (name, schema, collection, skipInit) { | ||
var _a; | ||
var Base = mongoose.models[name] || mongoose_1.model(name, schema, collection, skipInit); | ||
// eslint-disable-next-line | ||
// @ts-ignore | ||
return _a = /** @class */ (function (_super) { | ||
__extends(class_1, _super); | ||
function class_1() { | ||
return _super !== null && _super.apply(this, arguments) || this; | ||
} | ||
return class_1; | ||
}(Base)), | ||
_a.findConnections = function (conditions, paginationArgs, projection) { return __awaiter(void 0, void 0, void 0, function () { | ||
exports.fromRelayId = function (id) { | ||
var original = Buffer.from(id, 'base64').toString('utf-8'); | ||
var _a = original.split('.'), modelName = _a[0], objectId = _a[1]; | ||
if (objectId === undefined) { | ||
throw new Error('Invalid id string. Should be a base64 encoded string containing model name and object ID concatenated by `.`'); | ||
} | ||
return { modelName: modelName, objectId: objectId }; | ||
}; | ||
var EnhancedModel = /** @class */ (function () { | ||
function EnhancedModel() { | ||
} | ||
Object.defineProperty(EnhancedModel.prototype, "relayId", { | ||
get: function () { | ||
// Ignoring TS errors about `this` access as this is intended paradigm as defined by mongoose documentation: | ||
// https://mongoosejs.com/docs/guide.html#virtuals | ||
// eslint-disable-next-line | ||
// @ts-ignore | ||
return Buffer.from(this.constructor.modelName + "." + this._id.toString()).toString('base64'); | ||
}, | ||
enumerable: false, | ||
configurable: true | ||
}); | ||
EnhancedModel.findConnections = function (conditions, paginationArgs, projection) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var before, after, first, last, idQuery, query, count, dataQuery, hasNextPage, hasPreviousPage, data, pageInfo; | ||
@@ -87,6 +84,6 @@ return __generator(this, function (_a) { | ||
query = __assign(__assign({}, conditions), (Object.keys(idQuery).length === 0 ? {} : { _id: idQuery })); | ||
return [4 /*yield*/, Base.find(query).countDocuments()]; | ||
return [4 /*yield*/, this.find(query).countDocuments()]; | ||
case 1: | ||
count = _a.sent(); | ||
dataQuery = Base.find(query, projection); | ||
dataQuery = this.find(query, projection); | ||
hasNextPage = false; | ||
@@ -124,4 +121,6 @@ hasPreviousPage = false; | ||
}); | ||
}); }, | ||
_a; | ||
}; | ||
}); | ||
}; | ||
return EnhancedModel; | ||
}()); | ||
exports.EnhancedModel = EnhancedModel; |
{ | ||
"name": "relay-mongoose", | ||
"version": "0.1.0", | ||
"version": "0.2.0", | ||
"repository": "https://github.com/wadamek65/relay-mongoose.git", | ||
@@ -5,0 +5,0 @@ "author": "wadamek65 <wadamek65@gmail.com>", |
135
src/index.ts
@@ -1,2 +0,2 @@ | ||
import { Document, FilterQuery, model, Schema } from 'mongoose'; | ||
import { Document, FilterQuery } from 'mongoose'; | ||
import * as mongoose from 'mongoose'; | ||
@@ -28,4 +28,7 @@ | ||
export interface EnhancedModel<T extends Document, QueryHelpers = {}> extends mongoose.Model<T, QueryHelpers> { | ||
prototype: mongoose.Model<T, QueryHelpers>; | ||
export interface EnhancedDocument extends Document { | ||
relayId: string; | ||
} | ||
export interface EnhancedModel<T extends EnhancedDocument, QueryHelpers = {}> extends mongoose.Model<T, QueryHelpers> { | ||
findConnections( | ||
@@ -38,66 +41,82 @@ conditions: FilterQuery<T>, | ||
export const enhancedModel = <T extends Document, QueryHelpers = {}>(name: string, schema?: Schema, collection?: string, | ||
skipInit?: boolean | ||
): EnhancedModel<T, QueryHelpers> => { | ||
const Base: mongoose.Model<T> = mongoose.models[name] || model(name, schema, collection, skipInit); | ||
export const fromRelayId = (id: string): { modelName: string; objectId: string } => { | ||
const original = Buffer.from(id, 'base64').toString('utf-8'); | ||
const [modelName, objectId] = original.split('.'); | ||
if (objectId === undefined) { | ||
throw new Error( | ||
'Invalid id string. Should be a base64 encoded string containing model name and object ID concatenated by `.`' | ||
); | ||
} | ||
// eslint-disable-next-line | ||
// @ts-ignore | ||
return class extends Base { | ||
static findConnections = async ( | ||
conditions: FilterQuery<T>, | ||
paginationArgs: PaginationArgs, | ||
projection?: any | null | ||
): Promise<ConnectionDocuments<T>> => { | ||
const { before, after, first, last } = paginationArgs; | ||
const idQuery = { | ||
...(before !== undefined ? { $lt: mongoose.Types.ObjectId(before) } : {}), | ||
...(after !== undefined ? { $gt: mongoose.Types.ObjectId(after) } : {}) | ||
}; | ||
return { modelName, objectId }; | ||
}; | ||
const query = { | ||
...conditions, | ||
...(Object.keys(idQuery).length === 0 ? {} : { _id: idQuery }) | ||
}; | ||
export class EnhancedModel<T extends EnhancedDocument, QueryHelpers = {}> { | ||
get relayId() { | ||
// Ignoring TS errors about `this` access as this is intended paradigm as defined by mongoose documentation: | ||
// https://mongoosejs.com/docs/guide.html#virtuals | ||
// eslint-disable-next-line | ||
// @ts-ignore | ||
return Buffer.from(`${this.constructor.modelName}.${this._id.toString()}`).toString('base64'); | ||
} | ||
const count = await Base.find(query).countDocuments(); | ||
const dataQuery = Base.find(query, projection); | ||
static async findConnections<T extends Document>( | ||
conditions: FilterQuery<T>, | ||
paginationArgs: PaginationArgs, | ||
projection?: any | null | ||
): Promise<ConnectionDocuments<T>> { | ||
const { before, after, first, last } = paginationArgs; | ||
const idQuery = { | ||
...(before !== undefined ? { $lt: mongoose.Types.ObjectId(before) } : {}), | ||
...(after !== undefined ? { $gt: mongoose.Types.ObjectId(after) } : {}) | ||
}; | ||
let hasNextPage = false; | ||
let hasPreviousPage = false; | ||
const query = { | ||
...conditions, | ||
...(Object.keys(idQuery).length === 0 ? {} : { _id: idQuery }) | ||
}; | ||
if (first !== undefined && first < count) { | ||
dataQuery.limit(first); | ||
hasNextPage = true; | ||
} | ||
// eslint-disable-next-line | ||
// @ts-ignore | ||
const count = await this.find(query).countDocuments(); | ||
// eslint-disable-next-line | ||
// @ts-ignore | ||
const dataQuery = this.find(query, projection); | ||
if (last !== undefined && last < count) { | ||
dataQuery.skip(count - last); | ||
hasPreviousPage = true; | ||
if (hasNextPage) { | ||
hasNextPage = false; | ||
} | ||
let hasNextPage = false; | ||
let hasPreviousPage = false; | ||
if (first !== undefined && first < count) { | ||
dataQuery.limit(first); | ||
hasNextPage = true; | ||
} | ||
if (last !== undefined && last < count) { | ||
dataQuery.skip(count - last); | ||
hasPreviousPage = true; | ||
if (hasNextPage) { | ||
hasNextPage = false; | ||
} | ||
} | ||
const data = await dataQuery; | ||
const pageInfo = { | ||
hasNextPage, | ||
hasPreviousPage, | ||
...(data.length > 0 | ||
? { | ||
startCursor: data[0].id, | ||
endCursor: data[data.length - 1].id | ||
} | ||
: {}) | ||
}; | ||
const data = await dataQuery; | ||
const pageInfo = { | ||
hasNextPage, | ||
hasPreviousPage, | ||
...(data.length > 0 | ||
? { | ||
startCursor: data[0].id, | ||
endCursor: data[data.length - 1].id | ||
} | ||
: {}) | ||
}; | ||
return { | ||
edges: data.map(edge => ({ | ||
cursor: edge.id, | ||
node: edge | ||
})), | ||
pageInfo | ||
}; | ||
return { | ||
edges: data.map(edge => ({ | ||
cursor: edge.id, | ||
node: edge | ||
})), | ||
pageInfo | ||
}; | ||
}; | ||
}; | ||
} | ||
} |
@@ -5,7 +5,7 @@ import * as chai from 'chai'; | ||
import { EnhancedModel, enhancedModel } from '../src'; | ||
import { EnhancedDocument, EnhancedModel, fromRelayId } from '../src'; | ||
const expect = chai.expect; | ||
export interface TestSchemaInterface extends mongoose.Document { | ||
export interface TestSchemaInterface extends EnhancedDocument { | ||
field: string; | ||
@@ -19,4 +19,3 @@ } | ||
describe('relay-mongoose tests', () => { | ||
let model; | ||
let EnhancedTestModel: EnhancedModel<TestSchemaInterface>; | ||
let TestModel: EnhancedModel<TestSchemaInterface>; | ||
let mongo; | ||
@@ -31,5 +30,6 @@ let docs; | ||
await mongoose.connect(await mongo.getUri()); | ||
EnhancedTestModel = enhancedModel<TestSchemaInterface>('test', testSchema); | ||
docs = [...Array(15)].map(() => new EnhancedTestModel({ field: value })); | ||
await EnhancedTestModel.insertMany(docs); | ||
testSchema.loadClass(EnhancedModel); | ||
TestModel = mongoose.model('test', testSchema) as any; | ||
docs = [...Array(15)].map(() => new TestModel({ field: value })); | ||
await TestModel.insertMany(docs); | ||
firstId = docs[0].id; | ||
@@ -47,4 +47,4 @@ lastId = docs[docs.length - 1].id; | ||
const value = 'test'; | ||
await new EnhancedTestModel({ field: value }).save(); | ||
const result = await EnhancedTestModel.findOne({ field: value }); | ||
await new TestModel({ field: value }).save(); | ||
const result = await TestModel.findOne({ field: value }); | ||
expect(result.field).to.equal(value); | ||
@@ -54,3 +54,3 @@ }); | ||
it('should return everything', async () => { | ||
const result = await EnhancedTestModel.findConnections({ field: value }, {}); | ||
const result = await TestModel.findConnections({ field: value }, {}); | ||
expect(result.edges.length).to.equal(docs.length); | ||
@@ -64,2 +64,29 @@ expect(result.pageInfo.startCursor).to.equal(result.edges[0].cursor).to.equal(firstId); | ||
}); | ||
it('should return a custom relay Id', async () => { | ||
const result = await TestModel.findOne({ field: value }, {}); | ||
expect(result.relayId).not.to.equal(result.id); | ||
expect(result.relayId) | ||
.to.equal(Buffer.from(`${TestModel.modelName}.${result.id}`).toString('base64')) | ||
.to.equal(Buffer.from(`test.${result.id}`).toString('base64')); | ||
}); | ||
it('should decode relayId properly', async () => { | ||
const fakeModelName = 'fakeModel'; | ||
const fakeObjectId = 'fakeObjectId'; | ||
const { modelName, objectId } = fromRelayId(Buffer.from(`${fakeModelName}.${fakeObjectId}`).toString('base64')); | ||
expect(modelName).to.equal(fakeModelName); | ||
expect(objectId).to.equal(fakeObjectId); | ||
}); | ||
it('should throw error when decoding invalid relayId', async () => { | ||
const fakeModelName = 'fakeModel'; | ||
try { | ||
fromRelayId(Buffer.from(fakeModelName).toString('base64')); | ||
} catch (e) { | ||
expect(e.toString()).to.equal( | ||
'Error: Invalid id string. Should be a base64 encoded string containing model name and object ID concatenated by `.`' | ||
); | ||
} | ||
}); | ||
}); | ||
@@ -73,3 +100,3 @@ | ||
}; | ||
const result = await EnhancedTestModel.findConnections({ field: value }, args); | ||
const result = await TestModel.findConnections({ field: value }, args); | ||
expect(result.edges.length).to.equal(amount); | ||
@@ -89,3 +116,3 @@ expect(result.pageInfo.startCursor).to.equal(result.edges[0].cursor).to.equal(firstId); | ||
}; | ||
const result = await EnhancedTestModel.findConnections({ field: value }, args); | ||
const result = await TestModel.findConnections({ field: value }, args); | ||
expect(result.edges.length).to.equal(amount); | ||
@@ -107,3 +134,3 @@ expect(result.pageInfo.startCursor) | ||
const result = await EnhancedTestModel.findConnections({ field: value }, args); | ||
const result = await TestModel.findConnections({ field: value }, args); | ||
expect(result.edges.length).to.equal(beforeIndex - afterIndex - 1); | ||
@@ -122,3 +149,3 @@ expect(result.pageInfo.endCursor).to.equal(docs[beforeIndex - 1].id); | ||
const result = await EnhancedTestModel.findConnections({ field: value }, args); | ||
const result = await TestModel.findConnections({ field: value }, args); | ||
expect(result.edges.length).to.equal(docs.length - amount); | ||
@@ -134,3 +161,3 @@ expect(result.pageInfo.endCursor).to.equal(lastId); | ||
const result = await EnhancedTestModel.findConnections({ field: value }, args); | ||
const result = await TestModel.findConnections({ field: value }, args); | ||
expect(result.edges.length).to.equal(amount); | ||
@@ -146,3 +173,3 @@ expect(result.pageInfo.endCursor).to.equal(docs[amount + amount - 1].id); | ||
const result = await EnhancedTestModel.findConnections({ field: value }, args); | ||
const result = await TestModel.findConnections({ field: value }, args); | ||
expect(result.edges.length).to.equal(amount); | ||
@@ -164,3 +191,3 @@ expect(result.pageInfo.endCursor).to.equal(lastId); | ||
const result = await EnhancedTestModel.findConnections({ field: value }, args); | ||
const result = await TestModel.findConnections({ field: value }, args); | ||
expect(result.edges.length).to.equal(amount); | ||
@@ -178,3 +205,3 @@ expect(result.pageInfo.startCursor).to.equal(docs[0].id); | ||
const result = await EnhancedTestModel.findConnections({ field: value }, args); | ||
const result = await TestModel.findConnections({ field: value }, args); | ||
expect(result.edges.length).to.equal(amount); | ||
@@ -192,3 +219,3 @@ expect(result.pageInfo.endCursor).to.equal(docs[amount - 1].id); | ||
const result = await EnhancedTestModel.findConnections({ field: value }, args); | ||
const result = await TestModel.findConnections({ field: value }, args); | ||
expect(result.edges.length).to.equal(amount); | ||
@@ -195,0 +222,0 @@ expect(result.pageInfo.endCursor).to.equal(docs[beforeIndex - 1].id); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
20448
8
418