创建无法用于导入的猫鼬模型库

时间:2019-05-29 19:09:28

标签: node.js typescript mongoose

我一直在尝试创建mongo模型和帮助器类的库,然后将其作为npm模块与我们团队的其他成员共享。我遇到的问题是我从lib MongoConnector导入的主要代码,该代码处理配置信息并连接到mongo,我也导入了Model1并查找了所有可调用的方法。但是,当我从同一个库中导入Poller并将其传递给Model1时,一旦发现,promise就永远不会返回。

图书馆有4个文件

Model1.ts:

import mongoose from 'mongoose';

export interface IModel1 {
    draftId: string;
    [k:string]: any;
}

export interface IModel1DocumnetAttrs {
    draftId: string;
    data: string;
    createdBy?: String;
}

export interface IModel1Document extends mongoose.Document {
    draftId: String;
    createdBy: String;
    data: string;
}

export type IModel1Model = mongoose.Model<IModel1Document>;

const schema = new mongoose.Schema(
    {
        draftId: {type: String, index: {unique: true }},
        createdBy: String,
        data: String,
    },
    { versionKey: false, strict: false, timestamps: true, },
);

export const Model1: IModel1Model = mongoose.model<IModel1Document, IModel1Model>('Model1', schema);

MongoPoller.ts:

import * as Logger from 'bunyan';
import * as _ from 'lodash';
import mongoose from 'mongoose';

export type IMongoPollerCallback<T extends mongoose.Document> = (error: Error | undefined, docs?: T[]) => void;

export class MongoPoller<T extends mongoose.Document> {
    private query: any;
    private callback: IMongoPollerCallback<T>;
    private intervalId: any;
    private readonly log: Logger;
    constructor(private readonly pollingTime: number, private readonly model: typeof mongoose.Model, baseLogger: Logger) {
        this.callback = _.noop;
        this.log = baseLogger.child({ component: 'MongoPoller' });
    }

    public start(query: any, callback: IMongoPollerCallback<T>) {
        this.query = query;
        this.callback = callback;

        this.intervalId = setInterval(() => this.workFunction(), this.pollingTime);
    }

    public stop() {
        clearInterval(this.intervalId);
    }

    public async initial(query: any, callback: IMongoPollerCallback<T>) {
        this.query = query;
        this.callback = callback;
        this.log.info(`Mongo Poller started with initial, ${JSON.stringify(query)}`);

        return this.workFunction();
    }

    public continue() {
        this.log.info(`Mongo Poller told to continue, ${JSON.stringify(this.query)}`);
        this.intervalId = setTimeout(() => this.workFunction(), this.pollingTime);
    }

    private async workFunction() {
        try{
            this.log.info(`Mongo Poller executing workFunction, ${JSON.stringify(this.query)}`);
            const docsp =  this.model.find(this.query).exec();
            const docs = await docsp;
            this.log.info(`Mongo Poller executing workFunction callback with ${docs.length} docs, ${JSON.stringify(this.query)}`);

            this.callback(undefined, docs)

        }
        catch (e){
            this.log.info(`Mongo Poller executing workFunction callback with ERROR ${e.message}, ${JSON.stringify(this.query)}`);
            this.callback(undefined)
        }

    }
}

MongoDBConnector.ts:

import Promise = require('bluebird');
import * as Logger from 'bunyan';
import mongoose from 'mongoose';
import { decrypt, IKeyData } from './cryptoUtils';

interface IMongoDatabaseConfig {
    uri?: string;
    fullUri?: string;
    options?: object;
    username?: string;
    password?: string;
    initialDelayMSec?: number;
    finalDelayMSec?: number;
    triesBeforeFinalDelay?: number;
    autoIndex?: boolean;
    debug?:boolean
}

export class MongoDBConnector {
    private readonly mongoConfig: IMongoDatabaseConfig = {};
    private connectionPromise: Promise<any> | undefined;
    private readonly log: Logger;
    constructor(config: IMongoDatabaseConfig, baseLogger: Logger, keyData: IKeyData) {
        this.mongoConfig = config;
        mongoose.set('debug', this.mongoConfig.debug);
        mongoose.set('autoCreate', true);
        this.log = baseLogger.child({ component: 'MongoDBConnector' });
        Object.assign(this.mongoConfig, config);
        if (
            !this.mongoConfig.username ||
            this.mongoConfig.username.trim() === '' ||
            !this.mongoConfig.password ||
            this.mongoConfig.password.trim() === ''
        ) {
            this.mongoConfig.fullUri = `mongodb://${this.mongoConfig.uri}`;
        } else {
            this.mongoConfig.fullUri = `mongodb://${this.mongoConfig.username}:${decrypt(
                this.mongoConfig.password,keyData
            )}@${this.mongoConfig.uri}`;
        }
        this.mongoConfig.autoIndex = true;

        mongoose.Promise = global.Promise;
    }

    private connectToMongo(numTries: number = 1): any {
        return mongoose
            .connect(<string>this.mongoConfig.fullUri, this.mongoConfig.options)
            .then(() => {
                this.log.info('MongoDB Connected!');

                return mongoose.connection;
            })
            .catch((error: any) => {
                const timeoutDelayMSec =
                    numTries < <number>this.mongoConfig.triesBeforeFinalDelay
                        ? this.mongoConfig.initialDelayMSec
                        : this.mongoConfig.finalDelayMSec;
                this.log.error(`Issue connecting to MongoDB: ${error.message}`);
                this.log.info(
                    `Retrying connection to MongoDB (attempt #${numTries}). Reconnecting in ${timeoutDelayMSec} ms...`,
                );

                return Promise.delay(<number>timeoutDelayMSec).then(() => {
                    return this.connectToMongo(numTries + 1);
                });
            });
    }

    public connect() {
        if (!this.connectionPromise) {
            this.log.info(`Attempting to connect to MongoDB on ${this.mongoConfig.fullUri}`);
            this.connectionPromise = this.connectToMongo().catch((error: any) => {
                this.connectionPromise = undefined;
                throw error;
            });
        }

        return this.connectionPromise;
    }
}

index.ts:

export { Model1 } from './Model1';
export { MongoDBConnector} from './MongoDBConnector';
export {IMongoPollerCallback, MongoPoller}  from './MongoPoller';

主应用程序:

index.ts:

import * as config from './config/config.json';
import * as keyEventLogger from './logging/keyEventLogger';
import { loggerFactory } from './logging/loggerFactory';


import {
    DraftJudgment,
    IDraftDocument,
    IMongoPollerCallback,
    MongoDBConnector,
    MongoPoller
} from 'exploitmongomodels';

import mongoose from 'mongoose';

import { Promise as bluebird } from 'bluebird';
import * as keyData from './key.json';

global.Promise = bluebird;

const log = loggerFactory('main');

async function main() {
    // adding better logging for uncaught rejected promises
    process.on('unhandledRejection', err => {
        log.fatal({ err }, 'uncaught rejection');
    });

    process.on('uncaughtException', err => {
        log.fatal({ err }, 'uncaught exception');
    });

    const mongo = new MongoDBConnector(config.database.mongo, log, keyData);

    await mongo.connect();

    keyEventLogger.logKeyEvent(keyEventLogger.keyEvents.heartBeat, '');

    const d = await DraftJudgment.find({}).exec();
    console.log(d.length); //<- this prints 3
    //const j2 = mongoose.model('DraftJudgment');  <-- this throws an error

    //Set up heart beat for log files
    setInterval(
        () => keyEventLogger.logKeyEvent(keyEventLogger.keyEvents.heartBeat, ''),
        config.logger.heartbeatInterval,
    );

    const judgmentSubmitPoller = new MongoPoller<IDraftDocument>(config.pollingDurationInSec, DraftJudgment, log);

    const judgmentSubmitPollingCB: IMongoPollerCallback<IDraftDocument> = (
        error: Error | undefined,
        docs?: IDraftDocument[],
    ) => {
        if (error) {
            log.error(error, 'Error during judgment mongo polling');
            judgmentSubmitPoller.continue();

            return;
        }
        log.info(`Submit poller triggered submitted judgments`);
        if (!docs) {
            return;
        }
        //do something with the docs object

        log.info(`Submit poller triggered with ${docs.length} submitted judgments`);

        judgmentSubmitPoller.continue();
    };
    judgmentSubmitPoller.initial({ status: 'submitted' }, judgmentSubmitPollingCB);

    const judgmentPendingPoller = new MongoPoller<IDraftDocument>(config.pollingDurationInSec, DraftJudgment, log);

    const judgmentPendingPollingCB: IMongoPollerCallback<IDraftDocument> = (
        error: Error | undefined,
        docs?: IDraftDocument[],
    ) => {
        if (error) {
            log.error(error, 'Error during judgment mongo polling');
            judgmentSubmitPoller.continue();

            return;
        }
        if (!docs) {
            return;
        }

        //do something with the docs object

        log.info(`Pending poller triggered with ${docs.length} submitted judgments`);

        judgmentPendingPoller.continue();
    };
    //judgmentPendingPoller.initial({ $query: { status: 'pending' } }, judgmentPendingPollingCB);

    //tslint:disable-next-line
    while (true) {}
}

main();

在MongoPoller#workFunction中,docsp许诺永远无法解决。

1 个答案:

答案 0 :(得分:0)

这有点尴尬,问题在于main底部的while循环。由另一个开发人员添加;我们都认为有必要防止节点退出。嗯,事实并非如此,因为循环如此紧密,所以它从来没有为诺言解决提供时间。

生活和学习。